Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing Monoidal in terms of Applicative

Tags:

haskell

Typeclassopedia presents the following exercise:

Implement pure and (<*>) in terms of unit and (**), and vice versa.

Here's Monoidal and MyApplicative:

class Functor f => Monoidal f where
  u :: f ()                          -- using `u` rather than `unit` 
  dotdot :: f a -> f b -> f (a,b)    -- using instead of `(**)`

class Functor f => MyApplicative f where
  p     :: a -> f a                  -- using instead of `pure`
  apply :: f (a -> b) -> f a -> f b  -- using instead of `(<**>)`

First, let me show the Maybe-like data type:

data Option a = Some a 
                | None deriving Show

Then, I defined instance MyApplicative Option:

instance MyApplicative Option where
  p                = Some
  apply None _     = None
  apply _ None     = None
  apply (Some g) f = fmap g f 

Finally, my attempt at implementing Monoidal Option in terms of p and apply of MyApplicative:

instance Monoidal Option where
  u                        = p ()
  dotdot None _            = None 
  dotdot _ None            = None
  dotdot (Some x) (Some y) = Some id <*> Some (x, y)

Is this right? My implementation of dotdot with apply doesn't seem

instance Monoidal Option where
  u                        = p ()
  dotdot None _            = None 
  dotdot _ None            = None
  dotdot (Some x) (Some y) = apply (Some id) (Some (x, y))

In particular, I'm curious about how to properly implement dotdot :: f a -> f b -> f (a, b) with Applicative's (<*>) - in my case it's apply.

like image 760
Kevin Meredith Avatar asked Dec 19 '22 09:12

Kevin Meredith


1 Answers

Applicative is a neat alternative presentation of Monoidal. Both typeclasses are equivalent, and you can convert between the two without considering a specific data type like Option. The "neat alternative presentation" for Applicative is based on the following two equivalencies

pure a = fmap (const a) unit
unit = pure ()

ff <*> fa = fmap (\(f,a) -> f a) $ ff ** fa
fa ** fb = pure (,) <*> fa <*> fb

The trick to get this "neat alternative presentation" for Applicative is the same as the trick for zipWith - replace explicit types and constructors in the interface with things that the type or constructor can be passed into to recover what the original interface was.

unit :: f ()

is replaced with pure which we can substitute the type () and the constructor () :: () into to recover unit.

pure :: a  -> f a
pure    () :: f ()

And similarly (though not as straightforward) for substituting the type (a,b) and the constructor (,) :: a -> b -> (a,b) into liftA2 to recover **.

liftA2 :: (a -> b -> c) -> f a -> f b -> f c
liftA2    (,)           :: f a -> f b -> f (a,b)

Applicative then gets the nice <*> operator by lifting function application ($) :: (a -> b) -> a -> b into the functor.

(<*>) :: f (a -> b) -> f a -> f b
(<*>) = liftA2 ($)

Getting from <*> back to liftA2 is common enough that liftA2 is included in Control.Applicative. The <$> is infix fmap.

liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
liftA2 f a b = f <$> a <*> b
like image 68
Cirdec Avatar answered Jan 11 '23 11:01

Cirdec