In Monoid and Semigroup instances of Alternative Alt used.
Why we can't write instance without it?
{-# LANGUAGE FlexibleInstances #-}
instance Alternative f => Semigroup (f a) where
(<>) = <|>
instance Alternative f => Monoid (f a) where
mempty = empty
And if we can write that, can we then replace Alternative with (Monoid (f a), Applicative f) in functions?
CodePudding user response:
You can use it for deriving Monoid for any Alternative
{-# Language DerivingVia #-}
data F a = ..
deriving (Semigroup, Monoid)
via Alt F a
instance Functor F where ..
instance Applicative F where ..
instance Alternative F where ..
Alt is a newtype for good reason as there are many ways to describe Monoid behaviour for an applied type f a. For example Applicative lifting: Ap.
{-# Language DerivingVia #-}
data G a = ..
deriving (Semigroup, Monoid, Num, Bounded)
via Ap G a
instance Functor G where ..
instance Applicative G where ..
The instances you give are maximally overlapping, the Monoid instance of any applied type is now forced to be the Alternative instance, completely ignoring the a parameter.
There are many instances where this would not be correct, for example Semigroup a => Semigroup (Maybe a) is not the same as Alternative Maybe.
It is possible using a rather new feature QuantifiedConstraints to quantify over the argument of a type constructor forall x. Monoid (f x). This is not the same as Alternative but similar
{-# Language QuantifiedConstraints #-}
..
type Alternative' :: (Type -> Type) -> Constraint
class (forall x. Monoid (f x)) => Alternative' f
instance (forall x. Monoid (f x)) => Alternative' f
CodePudding user response:
You use the (<|>) :: Alternative f => f a -> f a -> f a and empty :: Alternative f => f a. As the signatures suggest, these are defined for items of type Applicative f => f a. You thus can only use these functions to process such items, and this thus requires that if you work with an f a, for eample to define mempty :: f a with mempty = empty, then that requires that f is a member of the Alternative typeclass.
That being said, while a lot data types can have a Semigroup and Monoid instance defined as how the Alternative is implemented, this is not per se the best instance. Yes Alternative is a monoid on applicative functors, but that should not per se be the monoid instance on these data types.
CodePudding user response:
If the instances you propose existed in that form, every type that matches f a would immediately defer to it. That includes types where it makes sense, but consider
newtype ResultsSum a = ResultsSum {funToSum :: a -> Int}
instance Semigroup (ResultsSum a) where
ResultsSum p <> ResultsSum q = ResultsSum $ \x -> p x q x
Unfortunately though, ResultsSum a matches f a. But it is not an Alternative; it isn't even a functor, and can't be (rather, it is Contravariant). However, the compiler doesn't take that into account when resolving instances: it just sees two instance declarations whose heads both purport to enable ResultsSum being a semigroup, which triggers an ambiguous-instance error.
Granted, this example could be addressed with {-# OVERLAPPING #-} pragmas, but it's always best to avoid instance overlaps as they can lead to strange. It's unnecessary, since you can also also derive those instances via the Alternative one. Though I personally would actually rather do it the other way around: define the Monoid instance first and then Alternative in terms of it.
