Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why WrappedMonad and WrappedArrow?

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?

like image 900
yong Avatar asked Feb 12 '15 01:02

yong


2 Answers

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.

like image 61
Ørjan Johansen Avatar answered Nov 17 '22 14:11

Ørjan Johansen


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.

like image 42
duplode Avatar answered Nov 17 '22 15:11

duplode