I'm playing around with formulating Applicative in terms of pure
and liftA2
(so that (<*>) = liftA2 id
becomes a derived combinator).
I can think of a bunch of candidate laws, but I'm not sure what the minimal set would be.
f <$> pure x = pure (f x)
f <$> liftA2 g x y = liftA2 ((f .) . g) x y
liftA2 f (pure x) y = f x <$> y
liftA2 f x (pure y) = liftA2 (flip f) (pure y) x
liftA2 f (g <$> x) (h <$> y) = liftA2 (\x y -> f (g x) (h y)) x y
Functor Laws If two sequential mapping operations are performed one after the other using two functions, the result should be the same as a single mapping operation with one function that is equivalent to applying the first function to the result of the second.
Applicative functors are the programming equivalent of lax monoidal functors with tensorial strength in category theory. Applicative functors were introduced in 2008 by Conor McBride and Ross Paterson in their paper Applicative programming with effects.
Functors apply a function to a wrapped value: Applicatives apply a wrapped function to a wrapped value: Monads apply a function that returns a wrapped value to a wrapped value. Monads have a function >>= (pronounced "bind") to do this.
Functor in Haskell is a typeclass that provides two methods – fmap and (<$) – for structure-preserving transformations. To implement a Functor instance for a data type, you need to provide a type-specific implementation of fmap – the function we already covered.
Based on McBride and Paterson's laws for Monoidal
(section 7) I'd suggest the following laws for liftA2
and pure
.
left and right identity
liftA2 (\_ y -> y) (pure x) fy = fy
liftA2 (\x _ -> x) fx (pure y) = fx
associativity
liftA2 id (liftA2 (\x y z -> f x y z) fx fy) fz =
liftA2 (flip id) fx (liftA2 (\y z x -> f x y z) fy fz)
naturality
liftA2 (\x y -> o (f x) (g y)) fx fy = liftA2 o (fmap f fx) (fmap g fy)
It isn't immediately apparent that these are sufficient to cover the relationship between fmap
and Applicative
's pure
and liftA2
. Let's see if we can prove from the above laws that
fmap f fx = liftA2 id (pure f) fx
We'll start by working on fmap f fx
. All of the following are equivalent.
fmap f fx
liftA2 (\x _ -> x) (fmap f fx) ( pure y ) -- by right identity
liftA2 (\x _ -> x) (fmap f fx) ( id (pure y)) -- id x = x by definition
liftA2 (\x _ -> x) (fmap f fx) (fmap id (pure y)) -- fmap id = id (Functor law)
liftA2 (\x y -> (\x _ -> x) (f x) (id y)) fx (pure y) -- by naturality
liftA2 (\x _ -> f x ) fx (pure y) -- apply constant function
At this point we've written fmap
in terms of liftA2
, pure
and any y
; fmap
is entirely determined by the above laws. The remainder of the as-yet-unproven proof is left by the irresolute author as an exercise for the determined reader.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With