monad stack with multiple of the same transformer - haskell

I was trying to write my own monad transformers where it would make sense to have multiple of the same monad transformer on the stack with different types. The issue can be illustrated with the reader monad.
The reader monad is offered as a way to hold a read only context of a given type
ex1 :: Reader Bool Bool
ex1 = ask
or
ex2 :: Reader Char Bool
ex2 = pure True
monad transformers allow less restrictive assumptions about the underlining monad
ex3 :: (MonadReader Bool m) => m Bool
ex3 = ask
However, what if I want to have more than 1 read only environment? I can write a function like
ex4 :: (MonadReader Bool m, MonadReader Char m) => m Bool
ex4 = ask
However, as far as I can tell, there is no way to run ex4 since
class Monad m => MonadReader r m | m -> r
means that each MonadReader has a unique reading type. Is there a standard work around for multiple transformers on the same stack? Should I try to avoid this entirely?

Use a transformer and lift to get to your inner monad:
import Control.Monad.Trans.Reader
import Control.Monad.Trans.Class (lift)
type MyMonad a = ReaderT Bool (Reader Char) a
askBool :: MyMonad Bool
askBool = ask
askChar :: MyMonad Char
askChar = lift ask
The code you presented didn't use any monad transformer (directly). It used the reader monad (which happens to be a transformer applied to the identity monad) and the MonadReader type class. As you noticed, the type function implied by MonadReader can't result in two different outputs (the environment types) for the same input (the monad m).

One way to deal with it in a relatively straightforward way is to create a type that represents the state you wanna keep track of. Say you want to keep track of both a Bool and a Char as in your example
data MyState = MyState { getBool :: Bool, getChar :: Char }
f :: MonadReader MyState m => m Bool
f = asks getBool
Others may have more advanced solutions!

Related

Unable to derive Applicative when combining two monad transformer stacks

I've written two monads for a domain-specific language I'm developing. The first is Lang, which is supposed to include everything needed to parse the language line by line. I knew I would want reader, writer, and state, so I used the RWS monad:
type LangLog = [String]
type LangState = [(String, String)]
type LangConfig = [(String, String)]
newtype Lang a = Lang { unLang :: RWS LangConfig LangLog LangState a }
deriving
( Functor
, Applicative
, Monad
, MonadReader LangConfig
, MonadWriter LangLog
, MonadState LangState
)
The second is Repl, which uses Haskeline to interact with a user:
newtype Repl a = Repl { unRepl :: MaybeT (InputT IO) a }
deriving
( Functor
, Applicative
, Monad
, MonadIO
)
Both seem to work individually (they compile and I've played around with their behavior in GHCi), but I've been unable to embed Lang into Repl to parse lines from the user. The main question is, how can I do that?
More specifically, if I write Repl to include Lang the way I originally intended:
newtype Repl a = Repl { unRepl :: MaybeT (InputT IO) (Lang a) }
deriving
( Functor
, Applicative
, Monad
, MonadIO
, MonadReader LangConfig
, MonadWriter LangLog
, MonadState LangState
)
It mostly typechecks, but I can't derive Applicative (required for Monad and all the rest).
Since I'm new to monad transformers and designing REPLs, I've been studying/cargo-culting from Glambda's Repl.hs and Monad.hs. I originally picked it because I will try to use GADTs for my expressions too. It includes a couple unfamiliar practices, which I've adopted but am totally open to changing:
newtype + GeneralizedNewtypeDeriving (is this dangerous?)
MaybeT to allow quitting the REPL with mzero
Here's my working code so far:
{- LANGUAGE GeneralizedNewtypeDeriving #-}
module Main where
import Control.Monad.RWS.Lazy
import Control.Monad.Trans.Maybe
import System.Console.Haskeline
-- Lang monad for parsing language line by line
type LangLog = [String]
type LangState = [(String, String)]
type LangConfig = [(String, String)]
newtype Lang a = Lang { unLang :: RWS LangConfig LangLog LangState a }
deriving
( Functor
, Applicative
, Monad
, MonadReader LangConfig
, MonadWriter LangLog
, MonadState LangState
)
-- Repl monad for responding to user input
newtype Repl a = Repl { unRepl :: MaybeT (InputT IO) (Lang a) }
deriving
( Functor
, Applicative
, Monad
, MonadIO
)
And a couple attempts to extend it. First, including Lang in Repl as mentioned above:
newtype Repl a = Repl { unRepl :: MaybeT (InputT IO) (Lang a) }
deriving
( Functor
, Applicative
)
-- Can't make a derived instance of ‘Functor Repl’
-- (even with cunning newtype deriving):
-- You need DeriveFunctor to derive an instance for this class
-- In the newtype declaration for ‘Repl’
--
-- After :set -XDeriveFunctor, it still complains:
--
-- Can't make a derived instance of ‘Applicative Repl’
-- (even with cunning newtype deriving):
-- cannot eta-reduce the representation type enough
-- In the newtype declaration for ‘Repl’
Next, trying to just use both of them at once:
-- Repl around Lang:
-- can't access Lang operations (get, put, ask, tell)
type ReplLang a = Repl (Lang a)
test1 :: ReplLang ()
test1 = do
liftIO $ putStrLn "can do liftIO here"
-- but not ask
return $ return ()
-- Lang around Repl:
-- can't access Repl operations (liftIO, getInputLine)
type LangRepl a = Lang (Repl a)
test2 :: LangRepl ()
test2 = do
_ <- ask -- can do ask
-- but not liftIO
return $ return ()
Not shown: I also tried various permutations of lift on the ask and putStrLn calls. Finally, to be sure this isn't an RWS-specific issue I tried writing Lang without it:
newtype Lang2 a = Lang2
{ unLang2 :: ReaderT LangConfig (WriterT LangLog (State LangState)) a
}
deriving
( Functor
, Applicative
)
That gives the same eta-reduce error.
So to recap, the main thing I want to know is how do I combine these two monads? Am I missing an obvious combination of lifts, or arranging the transformer stack wrong, or running into some deeper issue?
Here are a couple possibly-related questions I looked at:
Tidying up Monads - turning application of a monad transformer into newtype monad
Generalized Newtype DerivingGeneralized Newtype Deriving
Issue deriving MonadTrans for chained custom monad transformers
Update: my hand-wavy understanding of monad transformers was the main problem. Using RWST instead of RWS so LangT can be inserted between Repl and IO mostly solves it:
newtype LangT m a = LangT { unLangT :: RWST LangConfig LangLog LangState m a }
deriving
( Functor
, Applicative
, Monad
, MonadReader LangConfig
, MonadWriter LangLog
, MonadState LangState
)
type Lang2 a = LangT Identity a
newtype Repl2 a = Repl2 { unRepl2 :: MaybeT (LangT (InputT IO)) a }
deriving
( Functor
, Applicative
, Monad
-- , MonadIO -- ghc: No instance for (MonadIO (LangT (InputT IO)))
, MonadReader LangConfig
, MonadWriter LangLog
, MonadState LangState
)
The only remaining issue is I need to figure out how to make Repl2 an instance io MonadIO.
Update 2: All good now! Just needed to add MonadTrans to the list of instances derived for LangT.
You're trying to compose the two monads, one on top of the another. But in general monads don't compose this way. Let's have a look at a simplified version of your case. Let's assume we have just Maybe instead of MaybeT ... and Reader instead of Lang. So the type of your monad would be
Maybe (LangConfig -> a)
Now if this were a monad, we would have a total join function, which would have type
join :: Maybe (LangConfig -> Maybe (LangConfig -> a)) -> Maybe (LangConfig -> a)
And here a problem comes: What if the argument is a value Just f where
f :: LangConfig -> Maybe (LangConfig -> a)
and for some input f returns Nothing? There is no reasonable way how we could construct a meaningful value of Maybe (LangConfig -> a) from Just f. We need to read the LangConfig so that f can decide if its output will be Nothing or Just something, but within Maybe (LangConfig -> a) we can either return Nothing or read LangConfig, not both! So we can't have such a join function.
If you carefully look at monad transformers, you see that sometimes there is just one way how to combine two monads, and it's not their naive composition. In particular, both ReaderT r Maybe a and MaybeT (Reader r) a are isomorphic to r -> Maybe a. As we saw earlier, the reverse isn't a monad.
So the solution to your problem is to construct monad transformers instead of monads. You can either have both as monad transformers:
newtype LangT m a = Lang { unLang :: RWST LangConfig LangLog LangState m a }
newtype ReplT m a = Repl { unRepl :: MaybeT (InputT m) a }
and use them as LangT (ReplT IO) a or ReplT (LangT IO) a (as described in one of the comments, IO always has to be at the bottom of the stack). Or you can have just one of them (the outer one) as a transformer and another as a monad. But as you're using IO, the inner monad will have to internally include IO.
Note that there is a difference between LangT (ReplT IO) a and ReplT (LangT IO) a. It's similar to the difference between StateT s Maybe a and MaybeT (State s) a: If the former fails with mzero, neither result nor output state is produed. But in the latter fails with mzero, there is no result, but the state will remain available.

Lift a function with a monad parameter into a monad transformer

I want to lift a function like mask_ :: IO a -> IO a to create a function with this signature: lmask_ :: StateT Bool IO a -> StateT IO a.
My problem is, how to handle the callback/first parameter? Wouldn't the following code be incorrect since it would execute the callback before mask_'s code?
lmask_ :: StateT Bool IO a -> StateT Bool IO a
lmask_ m = do
r <- m
lift (mask_ (return r))
Is there some general way to do this? A helper like lift1 :: MonadTrans t => (m a -> m a) -> (t m a -> t m a)?
If we generalize lmask_ to get rid of the StateT Bool IO, we get something like this:
lift1 :: (Monad m, Monad (t m), MonadTrans t) => (m a -> m a) -> (t m a -> t m a)
lift1 f term = do
x <- term
lift (f (return x))
In general this is not possible without knowing something about the monad transformer. However, there is a way how to do this for all the standard monad transformers. See type class MonadBaseControl. It's superclass MonadBase defines what is the bottom monad in a monad transformer stack (which is IO for all stacks that include IO), and MonadBaseControl defines a way how to embed the monad into the base monad. Its instances are somewhat convoluted, but once they're defined, it's possible to lift all such functions like mask_.
In your case, package lifted-base uses the above construction to re-define the standard IO functions lifted to MonadBaseControl. In particular, there is mask_
mask_ :: MonadBaseControl IO m => m a -> m a
which can be specialized to StateT Bool IO a -> StateT Bool IO a, as StateT s has an instance of MonadBaseControl.
See also Lift a function and its argument to a different monadic context.

Implicit type coercion?

I don't understand why this code typechecks:
error1 :: ErrorT String (ReaderT Int IO) Int
error1 = asks id
fyi, the asks has this type:
asks :: Monad m => (r -> a) -> ReaderT r m a
On the other hand, I'm able to understand, that this code typechecks:
reader1 :: ReaderT Int IO Int
reader1 = asks id
id has type a -> a and there is an instance of Monad for IO, so the compiler can infer the type. That's clear for me.
The ErrorT is newtype and haskell spec states, (in the section about newtypes):
... it creates a distinct type that must be explicitly coerced to or
from the original type ...
According to my interpretation, I should be able to get the same type as in error1 only explicitly, with some coercion similar to this:
reader2 :: ReaderT Int IO (Either String Int)
reader2 = fmap (\i -> Right i) reader1
error2 :: ErrorT String (ReaderT Int IO) Int
error2 = ErrorT reader2
But, apparently, since the error1 typechecks just fine, there is some knowledge hidden from me. Can You help uncovering it for me?
The imports needed for running the example code:
import Control.Monad.Error (ErrorT(ErrorT))
import Control.Monad.Reader (ReaderT, asks)
The function asks is exported by two related modules with slightly different types. The version from Control.Monad.Trans.Reader (part of the transformers package), has the type given in the question:
asks :: Monad m => (r -> a) -> ReaderT r m a
However, the version used seems to be the one in the mtl package, from the Control.Monad.Reader module, which has the following, more general, type:
asks :: MonadReader r m => (r -> a) -> m a
So the example definition
error1 :: ErrorT String (ReaderT Int IO) Int
error1 = asks id
means that
MonadReader Int (ErrorT String (ReaderT Int IO))
must hold.
Also defined by mtl are the following instances for MonadReader:
instance Monad m => MonadReader r (ReaderT r m)
instance (Error e, MonadReader r m) => MonadReader r (ErrorT e m)
With these, the constraint above reduces to
(Error String, Monad IO)
which both hold as well.
I think part of your answer is that the monadic functions like asks, put, get, throwError, etc. in the mtl package are written to automatically lift themselves depending on how the monad stack is evaluated.
For example, the following function:
foo = do a <- asks id
if a < 0 then throwError "oops"
else return $ sqrt a
can have both of the types:
ErrorT String (ReaderT Double m) Double
ReaderT (ErrorT String m Double) Double
depending on the order in which runReaderT and runErrorT are run.
The most general type of this function is:
foo :: (MonadError [Char] m, MonadReader b m, Ord b, Floating b) => m b
which shows there is no a priori ordering to the monad layers.
In your example you gave a type signature which says that there is an ErrorT layer in your monad even though you didn't use the throwError function. That's just equivalent to adding the MonadError [Char] m constraint to the type signature.

Why does this code require the Monad constraint?

While building a monad stack with monad transformers to write a library, I hit a question about the behavior of it.
The following code won't pass the type checker:
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Foo (FooM, runFooM, foo) where
import Control.Applicative
import Control.Monad.Reader
newtype FooM m a = FooM { runFooM :: ReaderT Int m a }
deriving (Functor, Applicative, Monad, MonadReader Int)
foo :: FooM m Int
foo = do
x <- ask
return x
The error is:
$ ghc foo.hs
[1 of 1] Compiling Foo ( foo.hs, foo.o )
foo.hs:12:3:
No instance for (Monad m) arising from a do statement
Possible fix:
add (Monad m) to the context of
the type signature for foo :: FooM m Int
In a stmt of a 'do' block: x <- ask
In the expression:
do { x <- ask;
return x }
In an equation for ‘foo’:
foo
= do { x <- ask;
return x }
The fix is easy as GHC suggests, just adds Monad constraint to the foo function:
foo :: Monad m => FooM m Int
foo = do
x <- ask
return x
But here, the foo function only asks the FooM value to give its Int value and it is already an (automatically derived) MonadReader instance.
So I think Monad constraint is not required to m.
I guess this relates to the implementation of the monad transformers (I use mlt==2.2.1),
but I cannot figure out the exact reason.
I may miss something obvious though.
Could you explain why this doesn't pass the checker?
Thanks.
It's because the Monad instance for ReaderT is defined as
instance Monad m => Monad (ReaderT r m)
i.e. the type ReaderT r m is an instance of Monad only if the inne rm is an instance of Monad. That's why you cannot have an unconstrained m when using the Monad instance of ReaderT (which your FooM type is using via the deriving mechanism).
returns type is Monad m => a -> m a, hence the need for the constraint.
By the monad laws, foo ≡ ask, which will indeed work without the Monad constraint. But if you don't require Monad, then GHC can hardly make simplifications based on these laws, can it? Certainly not before type checking the code. And what you wrote is syntactic sugar for
foo = ask >>= \x -> return x
which requires both (>>=) :: Monad (FooM m) => FooM m Int -> (Int->FooM m Int) -> FooM m Int and return :: Monad (FooM m) => Int->FooM m Int.
Again, the >>= return does nothing whatsoever for a correct monad, but for a non-monad it isn't even defined and can thus not just be ignored.

Stuck in the State Monad

I want to create a graph structure using an IntMap of nodes and unique keys. This topic has been covered well here and here. I understand how the state monad works by basically wrapping a function of state -> (val,state) in a newtype so we can create a monad instance for it. I have read about the topic quite a bit. I still cant seem to get my head around how I can get unique (or just incremental) values throughout the execution of my program. It's easy enough to get a run of successive IDs, but once I "runState" to get out of the monad it seems like I'm back where I started with having to keep track of the current ID. I feel like I'm stuck in the monad. The other option I considered was to keep the entire IntMap and current "next" ID as the state, but that seems very "imperative" and extreme. This question is very similar but did not get many answers(or maybe I'm just missing something obvious). What is the idiomatic way to utilize the state monad to get unique ID throughout a program execution? Thanks.
Let's imagine we were to IO-ify the State monad. What would that look like? Our pure State monad is just a newtype around:
s -> (a, s)
Well, the IO version might do a little bit of side effects before returning the final values, which would look like:
s -> IO (a, s)
That pattern is so common it has a name, specifically StateT:
newtype StateT s m a = StateT { runStateT :: s -> m (a, s) }
The name has a T at the end because it is a monad Transformer. We call m the "base monad" and StateT s m the "transformed" monad.
StateT s m is only a Monad if m is a Monad:
instance (Monad m) => Monad (StateT s m) where {- great exercise -}
However, in addition to that, all monad transformers implement the MonadTrans class, defined as follows:
class MonadTrans t where
lift :: (Monad m) => m a -> t m a
instance MonadTrans (StateT s) where {- great exercise -}
If t is StateT s, then lift's type specializes to:
lift :: m a -> StateT s m a
In other words, it lets us "lift" an action in the base monad to become an action in the transformed monad.
So, for your specific problem, you want the StateT (IntMap k v) IO monad, which extends IO with additional State. Then you can write your entire program in this monad:
main = flip runStateT (initialState :: IntMap k v) $ do
m <- get -- retrieve the map
lift $ print m -- lift an IO action
(k, v) <- lift readLn
put (insert k v m)
Notice that I still use get and put. That's because the transformers package implements all the concepts I described and it generalizes the signatures of get and put to be:
get :: (Monad m) => StateT s m s
put :: (Monad m) => s -> StateT s m ()
That means they automatically work within StateT. transformers then just defines State as:
type State s = StateT s Identity
That means that you can use get and put for both State and StateT.
To learn more about monad transformers, I highly recommend Monad Transformers - Step by Step.

Resources