Why does the WrappedMonad and WrappedArrow types exist? Is it because Monads 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