'Applicative instance trying to use monoidal functors

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?



Solution 1:[1]

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).

Solution 2:[2]

I don't think that Pair is an Applicative the way you want it to be, Applicative states that

(<*>) :: f (a -> b) -> f a -> f b

should work for all functions in first position whereas you want

(<*>) :: Semigroup b => f (a -> b) -> f a -> f b.

If Pair was always a Semigroup (like Maybe or List for example) your reasoning would be sound, but you need the pre-requisite of the Pair-containee to be Semigroup.

Solution 3:[3]

Correct: Pair can't be made an Applicative in the way you want, because Applicative f demands that f a "feel applicative-y" for any a, even non-Semigroup as. Consider writing an alternative class and implementing it:

class CApplicative f where
    type C f
    pure :: C f a => a -> f a
    app  :: C f b => f (a -> b) -> f a -> f b

instance CApplicative Pair where
    type C Pair = Semigroup
    pure x = Pair x x
    app (Pure f g) p = fmap f p <> fmap g p

Solution 4:[4]

This can be derive since base 4.17.0.0, which includes the via types Generically and Generically1. This will derive the same instances as leftaroundabout writes:

{-# Language DeriveGeneric      #-}
{-# Language DerivingStrategies #-}
{-# Language DerivingVia        #-}

import Data.Monoid
import GHC.Generics

data Pair a = Pair a a
  deriving
  stock (Generic, Generic1)

  deriving (Semigroup, Monoid)
  via Generically (Pair a)

  deriving (Functor, Applicative)
  via Generically1 Pair

Alternatively you can lift Semigroup and Monoid through the Applicative

  deriving (Semigroup, Monoid, Num)
  via Ap Pair a

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 leftaroundabout
Solution 2 epsilonhalbe
Solution 3 Daniel Wagner
Solution 4 Iceland_jack