3

Indeed it does:

λ :i Applicative class Functor f => Applicative (f :: * -> *) where 

At the same time:

fmap f x = pure f <*> x 

— by the laws of Applicative we can define fmap from pure & <*>.

I don't get why I should tediously define fmap every time I want an Applicative if, really, fmap can be automatically set up in terms of pure and <*>.

I gather it would be necessary if pure or <*> were somehow dependent on the definition of fmap but I fail to see why they have to.

2
  • 3
    Do you want your applicative to work with fmap? There's no reason for it not to inherit Functor, because of the trivial implementation you've described. Commented Apr 27, 2017 at 7:44
  • Yeah, defining fmap is about the least tedious thing you can do with any type where it's at all possible. Commented Apr 27, 2017 at 14:02

2 Answers 2

8

While fmap can be derived from pure and <*>, it is generally not the most efficient approach. Compare:

fmap :: (a -> b) -> Maybe a -> Maybe b fmap f Nothing = Nothing fmap f (Just x) = Just (f x) 

with the work done using Applicative tools:

fmap :: (a -> b) -> Maybe a -> Maybe b -- inlining pure and <*> in: fmap f x = pure f <*> x fmap f x = case (Just f) of Nothing -> Nothing Just f' -> case x of Nothing -> Nothing Just x' -> Just (f' x') 

Pointlessly wrapping something up in a constructor just to do a pattern-match against it.

So, clearly it is useful to be able to define fmap independently of the Applicative functions. That could be done by making a single typeclass with all three functions, using a default implementation for fmap that you could override. However, there are types that make good Functor instances but not good Applicative instances, so you may need to implement just one. Thus, two typeclasses.

And since there are no types with Applicative instances but without Functor instances, you should be able to treat an Applicative as though it were a Functor, if you like; hence the extension relationship between the two.

However, if you tire of implementing Functor, you can (in most cases) ask GHC to derive the only possible implementation of Functor for you, with

{-# LANGUAGE DeriveFunctor #-} data Boring a = Boring a deriving Functor 
Sign up to request clarification or add additional context in comments.

2 Comments

In most of the cases where data Boring a = Boring a deriving Functor, a standalone deriving instance Functor Boring will still work. It's really rare to have a functor where GHC won't be able to derive the functor instance in some way, and even in those cases it will be peanuts do write it by hand, compared to the Applicative instance.
I would expect GHC, with -O, to simplify the nested case, cos case (Just f) of ... will always pick the Just branch. Indeed SPJ implies that it does in fact do this optimisation in this talk youtu.be/uR_VzYxvbxg?t=43m59s . Of course it won't necessarily be able to do that rewrite for all applicatives.
2

While there are proposals to make it's easier https://ghc.haskell.org/trac/ghc/wiki/IntrinsicSuperclasses the "default instances" problem itself is very difficult.

One challenge is how to deal with common superclasses:

fmap f x = pure f <*> x -- using Applicative fmap f x = runIdentity (traverse (Identity . f) x) -- using Traversable fmap f x = x >>= (return . f) -- using Monad 

Which one to pick?

So the best we can do now is to provide fmapDefault (as Data.Traversable) does; or use pure f <*> x; or fmapRep from Data.Functor.Rep when applicable.

1 Comment

fmap f x = x >>= (return . f)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.