1

I'm in the Haskell Book chapter on Applicative. I am writing the Applicative instance for ZipList and I know I overthought it and am changing my approach. My old approach was:

data List a = Nil | Cons a (List a) deriving (Eq, Show)

newtype ZipList' a =
    ZipList' (List a)
    deriving (Eq, Show)

instance Applicative ZipList' where
    pure a = ZipList' (Cons a Nil)
    ZipList' Nil <*> _ = ZipList' Nil
    _ <*> ZipList' Nil = ZipList' Nil
    (ZipList' (Cons a bs)) <*> (ZipList' (Cons a' bs')) = (ZipList' (Cons (a(a')) Nil)) `mappend` (ZipList' bs <*> ZipList' bs')

I get the error:

 No instance for (Monoid b) arising from a use of ‘mappend’
      Possible fix:
        add (Monoid b) to the context of
          the type signature for:
            (<*>) :: forall a b. ZipList' (a -> b) -> ZipList' a -> ZipList' b
    • In the expression:
        (ZipList' (Cons (a (a')) Nil))
          `mappend` (ZipList' bs <*> ZipList' bs')
      In an equation for ‘<*>’:
          (ZipList' (Cons a bs)) <*> (ZipList' (Cons a' bs'))
            = (ZipList' (Cons (a (a')) Nil))
                `mappend` (ZipList' bs <*> ZipList' bs')
      In the instance declaration for ‘Applicative ZipList'’

I think this is because the mappend uses the Monoid for ZipList which is:

instance Monoid a => Monoid (ZipList' a) where
    mempty = pure mempty
    mappend = liftA2 mappend

which places a Monoid constraint on the a. In the Applicative instance I can't add the a to the class instance without it ceasing to be a proper Applicative instance definition. I know that the solution is incorrect, but it did make me think "How would I add a Monoid constraint to the argument to ZipList in the Applicative instance?

duplode
  • 33,731
  • 7
  • 79
  • 150
tesserakt
  • 3,231
  • 4
  • 27
  • 40

2 Answers2

2

There's no need to append anything here. Don't build a list Cons x Nil and append list y, just build Cons x y.

let ZipList bs'' = ZipList' bs <*> ZipList' bs'
in ZipList' (Cons (a a') bs'')

About the monoid-related error. It depends on which instance you are using for your type ZipList'.

If you use something like

instance Monoid (ZipList' a) where
   mempty = ZipList' Nil
   mappend (ZipList' Nil) zs = zs
   mappend (ZipList' (Cons x xs)) zs = let
      ZipList' ys = mappend (Ziplist' xs) zs
      in ZipList' (Cons x ys)

then there is no need for Monoid a.

I think it all depends on what kind of monoid you want. The Monoid (ZipList' a) you mentioned appends each component of the two lists in a pointwise fashion, and for that we do need Monoid a. However, in the applicative instance you do not need that kind of append, you need list concatenation. This could be achieved using the above monoid instance, instead.

That being said: the instance you posted, working pointwise, is probably the most natural for ZipList. Nothing however stops you to define a non-instance function

appendZipList' :: ZipList' a -> ZipList' a -> ZipList' a

and use that in your applicative instance, without relying on monoids.

Otherwise, you could define instance Monoid (List a) for the concatenating append, and use that in the applicative instance

let ZipList' bs'' = ZipList' bs <*> ZipList' bs'
in ZipList' (Cons (a a') Nil `mappend` bs'')
chi
  • 111,837
  • 3
  • 133
  • 218
  • I ended up with some non-instance functions to zip the list and then throw that result into a `ZipList'` – tesserakt Apr 12 '18 at 15:40
0

I ended up eliminating the Monoid requirement and using a function on Lists which walked them and applied the funcs from the left to the values on the right.

zipForMyList :: List (t -> a) -> List t -> List a
zipForMyList _ Nil                   = Nil
zipForMyList Nil _                   = Nil
zipForMyList (Cons f fs) (Cons a as) = (Cons (f a) (zipForMyList fs as))

repeatMyList :: p -> List p
repeatMyList x = xs where xs = Cons x xs

instance Applicative ZipList' where
    pure a = ZipList' (Cons a Nil)
    ZipList' Nil <*> _ = ZipList' Nil
    _ <*> ZipList' Nil = ZipList' Nil
    (ZipList' fs) <*> (ZipList' as) = ZipList' (zipForMyList fs as)
tesserakt
  • 3,231
  • 4
  • 27
  • 40