Reputation: 1469
I am trying to build a Monad
instance of the following type:
data CmdY ρ η ω = CmdY (Reader ρ ((Stream (Of CmdSpec) η ω)))
where Stream
and Of
come from Streaming
in the streaming
package.
I have successfully written Functor
Applicative
instances:
instance (Monad η) => Functor (CmdY ρ η) where
fmap :: (α -> β) -> CmdY ρ η α -> CmdY ρ η β
fmap f (CmdY a) = CmdY $ (fmap f) <$> a
instance (Monad η) => Applicative (CmdY ρ η) where
pure = CmdY . pure . return
(<*>) :: CmdY ρ η (α -> β) -> CmdY ρ η α -> CmdY ρ η β
(CmdY f) <*> (CmdY a) = let ff = (<*>) <$> f
in CmdY $ ff <*> a
But I'm going round in circles trying to implement the bind (>>=)
of the Monad.
I have a function to evaluate a CmdY and give me a stream & result:
runCmdY :: (MonadIO η, MonadIO μ, MonadReader (ProcExecCtxt μ) ψ) =>
CmdY (ProcExecCtxt μ) η ω -> ψ (Stream (Of CmdSpec) η ω)
But given a bind of the form a >>= f
, a is of type CmdY (ProcExecCtxt η) η α
while f is of type α -> CmdY (ProcExecCtxt η) η β
, I need to get something of type α to feed to my f, and I am failing to get there.
ProcExecCtxt m
here is an execution context; it provides a means to evaluate cmds within the monad m.
I'm sure I'm missing something obvious (or at least, I'm hoping it will be obvious once I see it); but I'd grateful for any pointers.
Thanks,
Upvotes: 1
Views: 86
Reputation: 48591
Reader ρ (Stream (Of CmdSpec) η ω)
is really just
ρ -> Stream (Of CmdSpec) η ω
There's a third way to spell that type that probably makes more sense in this context:
ReaderT ρ (Stream (Of CmdSpec) η) ω
using Control.Monad.Trans.Reader.ReaderT
(also exported by Control.Monad.Reader
), a monad transformer defined
newtype ReaderT e m a = ReaderT
{ runReaderT :: e -> m a }
Since ReaderT e
is a monad transformer, ReaderT e m
is a monad whenever m
is. Therefore, it will provide all the instances you want for free, and a couple more. Indeed, using
{-# language GeneralizedNewtypeDeriving #-}
you can write
newtype CmdY ρ η ω = CmdY
{ unCmdY :: ReaderT ρ (Stream (Of CmdSpec) η) ω }
deriving (Functor, Applicative, Monad
, Alternative, MonadPlus, MonadReader ρ)
Or, if you like, you can define the instances manually by wrapping and unwrapping CmdY
:
instance Monad η => Functor (CmdY ρ η) where
fmap f (CmdY m) = CmdY (fmap f m)
instance Monad η => Applicative (CmdY ρ η) where
pure = CmdY . pure
CmdY fs <*> CmdY xs = CmdY (fs <*> xs)
instance Monad η => Monad (CmdY ρ η) where
CmdY m >>= f = CmdY $ m >>= unCmdY . f
CmdY ρ
is itself a monad transformer:
import Control.Monad.Trans.Class
instance MonadTrans (CmdY ρ) where
lift = CmdY . lift . lift
Upvotes: 3
Reputation: 2317
Data.Functor.Compose
implements your Functor
and Applicative
instances.
ReaderT
implements a Monad
instance for you.
Upvotes: 0