How to test the homomorphism law of an Applicative instance?

590 views Asked by At

I'm doing the exercises from Typeclassopedia; in the Applicative section, I write ZipList's pure function, and check whether it follows the Applicative Laws.

I've checked:

  • identity law
  • interchange law
  • composition law

But when I try to check the "homomorphism" law, I find that GHCi doesn't get the result as an MZipList.

I think this is because I miss to designate the pure to my Applicative type class. How can I run a pure function without <*> it to Applicative immediately?

Here's the MZipList definition and class instances:

newtype MZipList a = MZipList { getZipList :: [a] }
    deriving (Show)

instance Functor MZipList where
  fmap gs x = pure gs <*> x

instance Applicative MZipList where
  pure a= MZipList (repeat a)
  (MZipList gs) <*> (MZipList xs) = MZipList (zipWith ($) gs xs)

When I check the "Interchange" law, for example:

*Main> (MZipList [(*2),(*3),(*4)]) <*> pure (2)
MZipList {getZipList = [4,6,8]}
*Main> pure ($ 2) <*> (MZipList [(*2),(*3),(*4)])
MZipList {getZipList = [4,6,8]}

But when I check the "Homomorphism" law , the MZipList's pure is not called:

*Main> pure (*2) <*> pure 2
4
*Main>  pure ((*2) 2)
4
*Main>

Why is that?

1

There are 1 answers

0
Shoe On

What is pure?

pure is simply a function to "insert" an object inside a specific Applicative monad. For example in:

test :: [Int]
test = pure 1 -- [1]

we are inserting 1 into the list monad, which results in the singleton [1]. If you have already read about the Monad class, then pure is basically the same as return (if you haven't don't worry).

Your instance of Applicative seems to be working fine.

Your tests

When running commands in GHCi, you are basically in the IO monad, which is also an Applicative. So in general, pure x returns an IO (type of x).

In:

pure (*2) <*> pure 2

you are "putting" (*2) in an IO object, then putting 2 in an IO object as well, and finally calling <*> as defined in the instance Applicative IO.

You are not testing your MZipList instance.

In the second example you are simply calling:

pure ((*2) 2)

If you recall, (*2) 2 simply applies (*2) to 2, thus really executing 2 * 2. So your call is actually:

pure 4

which, in GHCi (still in the context of the IO monad) returns an IO Int object.

How to test properly

To test the "homomorphism" law, you just need to give the compiler a small hint as to what type you really want:

So instead of:

pure (*2) <*> pure 2

you would write:

pure (*2) <*> (pure 2 :: MZipList Int)

Live demo