Why does the WrappedMonad
and WrappedArrow
types exist? Is it because Monad
s were not Applicative
? Given that WrappedArrow
exists, should the instance
Arrow a => Applicative (Arrow a b)
simply be built into the Haskell itself the same way that Applicative
is now a superclass of Monad
?
Pretty much so for WrappedMonad
. I guess it is becoming (and perhaps already was) essentially obsolete. But WrappedArrow
is more difficult, because Arrow
types and Applicative
types have different kinds, * -> * -> *
vs. * -> *
. And because of the way GHC instance resolution works, adding the instance (I assume the extra Arrow
was a typo)
instance Arrow a => Applicative (a b)
would mean that no type constructor with two or more arguments could then give an Applicative
without also giving an Arrow
- that seems rather drastic.
The reverse option of adding a superclass Applicative (a b) =>
to Arrow a
would seem more palatable - except you cannot have superclasses with a forall'ed type like b
. Such superclasses would be useful for other things as well, and have been suggested many times, so I assume it is hard to implement well.
I feel the DerivingVia
extension gives a new lease of life to this kind of newtype wrapper. Let's suppose that, for whatever reason, we want to write Monad
and MonadPlus
instances for a type and then piggyback on them for Applicative
and Alternative
:
newtype StT s m a = StT { runStT :: s -> m (a, s) }
deriving Functor
instance Monad m => Monad (StT s m) where
return a = StT $ \s -> return (a, s)
m >>= f = StT $ \s -> m `runStT` s >>= \(a, t) -> f a `runStT` t
instance MonadPlus m => MonadPlus (StT s m) where
mzero = StT $ \_ -> mzero
m `mplus` n = StT $ \s -> (m `runStT` s) `mplus` (n `runStT` s)
Ordinarily, we'd have to write boilerplate instances:
instance Monad m => Applicative (StT s m) where
pure = return
(<*>) = ap
instance MonadPlus m => Alternative (StT s m) where
empty = mzero
(<|>) = mplus
With DerivingVia
, WrappedMonad
can be used to spell it out in a way that, to my eyes, is much nicer.
newtype StT s m a = StT { runStT :: s -> m (a, s) }
deriving Functor
deriving (Applicative, Alternative) via WrappedMonad (StT s m)
Note that preexisting deriving clauses are unaffected. Another nice touch is that the necessary superclass constraints are inferred.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With