15

I think I kind of understand how applicative functors work in Haskell and I'm using them for basic datatypes (Maybe, Either...). However, I found this question with the following example:

withPool pool = bracket (takeConn pool) (putConn pool)

can be rewritten in applicative style:

withPool = bracket <$> takeConn <*> putConn

I was surprised it compiled and indeed it works as expected, but could somebody tell me which Applicative Functor is used for this and how is it defined?

Update: I think I figured out how it works, but I have no idea where is it defined.

Community
  • 1
  • 1
ondra
  • 9,122
  • 1
  • 25
  • 34

1 Answers1

17

Unify f = (a ->) in the type signatures:

fmap :: (b -> c) -> (a -> b) -> (a -> c)
pure :: b -> (a -> b)
(<*>) :: (a -> b -> c) -> (a -> b) -> (a -> c)

The only reason the declarations are syntactically different, ((->) a) vs (a ->), is that you aren't allowed to take sections at the type level. So you end up with these, after chasing types:

instance Functor ((->) a) where
    fmap = (.)

instance Applicative ((->) a) where
    pure = const
    f <*> g = \x -> f x $ g x

I'm pretty sure the Functor instance is in Data.Functor, and the Applicative instance is in Control.Applicative. The Monad instance for ((->) a) is the only one in a strange spot, in Control.Monad.Instances, instead of Control.Monad. At least if I recall correctly.

Carl
  • 26,500
  • 4
  • 65
  • 86
  • 9
    For what it's worth, this is in fact the reader monad, minus a `newtype` wrapper--the "shared parameter" here is the same as the "environment" for `Reader`. In a similar way, `Either err` is the the error monad and `(,) w` is writer. – C. A. McCann Jan 06 '12 at 20:17
  • @C.A.McCann It's worth pointing out, and I forgot to. Thanks for adding that. – Carl Jan 07 '12 at 02:16