I'm learning Haskell and trying to do exercises from book Haskell Programming from first principles and I'm stack trying to write applicative for Pair type
data Pair a = Pair a a deriving Show
I have seen some other examples on web but I'm trying somewhat different applicative functor, I'm trying to utilize monoidal structure of this type. Here is what I have
data Pair a = Pair a a deriving (Show, Eq)
instance Functor Pair where
fmap f (Pair x y) = Pair (f x) (f y)
instance Semigroup a => Semigroup (Pair a) where
(Pair x y) <> (Pair x' y') = Pair (x <> x') (y <> y')
instance Applicative Pair where
pure x = Pair x x
(Pair f g) <*> p = fmap f p <> fmap g p
Unfortunately this will not compile:
* No instance for (Semigroup b) arising from a use of `<>'
Possible fix:
add (Semigroup b) to the context of
the type signature for:
(<*>) :: forall a b. Pair (a -> b) -> Pair a -> Pair b
* In the expression: fmap f p <> fmap g p
In an equation for `<*>': (Pair f g) <*> p = fmap f p <> fmap g p
In the instance declaration for `Applicative Pair'
And this is where I'm stack; I don't see how can I add typeclass constraint to Applicative definition and I thought that making type Pair instance of Semigroup is enough.
Other solutions that I have seen are like
Pair (f g) <*> Pair x y = Pair (f x) (g y)
but these solutions don't utilize monoidal part of Pair type
Is it even possible to make this applicative the way I't trying?
Although it's true that Applicative
is the class representing monoidal functors (specifically, Hask endofunctors which are monoidal), Allen&Moronuki present this unfortunately in a way that seems to suggest a direct relation between the Monoid
and Applicative
classes. There is, in general, no such relation! (The Writer
type does define one particular Applicative
instance based on the Monoid
class, but that's an extremely special case.)
This spawned a rather extended discussion at another SO question.
What the “monoidal” in “monoidal functor” refers to is a monoidal structure on the category's objects, i.e. on Haskell types. Namely, you can combine any two types to a tuple-type. This has per se nothing whatsoever to do with the Monoid
class, which is about combining two values of a single type to a value of the same type.
Pair
does allow an Applicative
instance, but you can't base it on the Semigroup
instance, although the definition actually looks quite similar:
instance Applicative Pair where
pure x = Pair x x
Pair f g <*> Pair p q = Pair (f p) (g q)
However, you can now define the Semigroup
instance in terms of this:
instance Semigroup a => Semigroup (Pair a) where
(<>) = liftA2 (<>)
That, indeed, is a valid Semigroup
instance for any applicative, but it's usually not the definition you want (often, containers have a natural combination operation that never touches the contained elements, e.g. list concatenation).