I have learnt about Monoidal being an alternative way to represent Applicative not so long ago. There is an interesting question on Typeclassopedia:
- (Tricky) Prove that given your implementations from the first exercise [
pureand(<*>)written down usingunitand(**)and the other way around], the usualApplicativelaws and theMonoidallaws stated above are equivalent.
Here are these classes and laws:
-- A note from https://wiki.haskell.org/Typeclassopedia#Alternative_formulation:
-- In this and the following laws, ≅ refers to isomorphism rather than equality.
-- In particular we consider (x,()) ≅ x ≅ ((),x) and ((x,y),z) ≅ (x,(y,z)).
-- Monoidal.
class Functor f => Monoidal f where
unit :: f ()
(**) :: f a -> f b -> f (a,b)
-- unit ** v ≅ v - Left Identity.
-- u ** unit ≅ u - Right Identity.
-- u ** (v ** w) ≅ (u ** v) ** w - Associativity.
-- Applicative.
class Functor f => Applicative f where
pure :: a -> f a
infixl 4 <*>, ...
(<*>) :: f (a -> b) -> f a -> f b
...
-- pure id <*> v = v - Identity.
-- pure f <*> pure x = pure (f x) - Homomorphism.
-- u <*> pure y = pure ($ y) <*> u - Interchange.
-- u <*> (v <*> w) = pure (.) <*> u <*> v <*> w - Composition.
Writing down combinators using others is no big deal:
unit = pure ()
f ** g = (,) <$> f <*> g = liftA2 (,) f g
pure x = const x <$> unit
f <*> g = uncurry ($) <$> (f ** g)
Here is my understanding of why the laws are telling us the same thing:
u <*> pure y = pure ($ y) <*> u -- Interchange: Applicative law.
The first thing we shall notice is that ($ y) ≅ y (more formally: (y -> a) -> a ≅ y). Having that in mind, Interchange law simply tells us that (a, b) ≅ (b, a).
pure id <*> v = v -- Identity: Applicative law.
I reckon id to be something of a unit itself as it is the only inhabitant of type forall a. a -> a. Therefore, this law gives us the Left Identity:
unit ** v = v -- Left Identity: Monoidal law.
Now we can use that (a, b) ≅ (b, a) to write the Right Identity down:
u ** unit = u -- Right Identity: Monoidal law.
The Composition law:
u <*> (v <*> w) = pure (.) <*> u <*> v <*> w -- Composition: Applicative law.
I reckon this law to tell the same thing as Associativity for Monoidal:
u ** (v ** w) ≅ (u ** v) ** w
That is, (a, (b, c)) ≅ ((a, b), c). Applicative just adds a layer of application.
So, we have covered all of the Monoidal laws. I believe there is no need to do it the other way around as we are going to use the same isomorphisms. But one could have noticed something odd - we did not use the Homomorphism Applicative law:
pure f <*> pure x = pure (f x)
I tried understanding Homomorphism in terms of the Naturality free theorem for Monoidal:
fmap (g *** h) (u ** v) = fmap g u ** fmap h v
But it seems odd as Homomorphism does not deal with side-effects, yet Naturality works with them just fine.
So, I have 3 questions:
- Is my reasoning right?
- Where does Homomorphism stand in this picture?
- How can we understand the Naturality free theorem in terms of
Applicative?