user3416536
user3416536

Reputation: 1469

Building a monad around Streaming & Reader

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

Answers (2)

dfeuer
dfeuer

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

Gurkenglas
Gurkenglas

Reputation: 2317

Data.Functor.Compose implements your Functor and Applicative instances.

ReaderT implements a Monad instance for you.

Upvotes: 0

Related Questions