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.