I am encountering a problem with Monad Transformers, but I think it's helpful to include some context of how I got to the state I'm currently in, so I'll start with a rough explanation of my program:
The project is an interpreter for a simple (toy) programming language. I have a monad that is used to represent evaluation. It has a definition that looks like:
type Eval a = ReaderT Environment (ExceptT String (State ProgState a))
This works quite nicely, and I can happy write an evaluation function:
eval :: Expr -> Eval Value
eval (Apply l r) = ...
eval ...
The Value datatype has a slight quirk in that I embed Haskell functions of type Value -> EvalM Value. To do this I added a generic type parameter to the definition, which I then instantiate with EvalM:
data Value' m
= IntVal Int
...
| Builtin (Value' m -> m (Value' m))
type Value = Value' EvalM
Things were going well, but then I had to write a function that heavily interleaved code using the Eval monad with IO operations. This looked kinda horrendous:
case runEval ({-- some computation--}) of
Right (val, state') -> do
result <- -- IO stuff here
case runEvaL {-- something involving result --} of
...
Left err -> ...
The function had like 5 levels of nesting, and was also recursive... definitely ugly :(. I hoped adapting to use a Monad Transformer would be the solution:
type EvalT m = ReaderT Environment (ExceptT String (StateT ProgState m))
This refactor was relatively painless: mostly it involved changing type-signatures rather than actual code, however there was a problem: Builtin. Given a expression that was applying argument x to a value of the form Builtin f, the eval function would simply return f x. However, this has type Eval Value, but the refactored eval needs to have type-signature:
eval :: Monad m => EvalT m Value
As far as Fixing this (i.e. making it typecheck) is concerned, I can think of a couple solutions each of which has a problem:
Implementing some kind of analog to lift where I can take Eval a to EvalT m a.
Problem: I'm not aware of how to do this (or if it's even possible)
Changing the Value type so that it is indexed by an inner monad, i.e. Value m = Value' (EvalT m).
Problem: now anything containing a Value m has to be
parameterized by m. I feel that it would unnecessarily clutters up the type-signatures of
anything containing a Value, which is a problem given the initial
motivation to do this change was cleaning up my code.
Of course, there may be a much better solution that I haven't thought of yet. Any feedback/suggestions are appreciated :).
You might like the mmorph package.
-- since State s = StateT s Identity, it's probably also the case
-- that Eval = EvalT Identity, under some light assumptions about
-- typos in the question
liftBuiltin :: Monad m => Eval a -> EvalT m a
liftBuiltin = hoist (hoist (hoist generalize))
Alternately, you could store a polymorphic function in your value. One way would be to parameterize over the transformer.
data Value' t = ... | Builtin (forall m. Monad m => Value' t -> t m (Value' t)
type Value = Value' EvalT
Another is to use mtl-style constraints.
data Value = ... | Builtin (forall m. (MonadReader Environment m, MonadError String m, MonadState ProgState m) => Value -> m Value)
This last one, though verbose, looks pretty nice to me; I'd probably start there.
Related
I am going though the following paper: Monad Transformers Step by Step. In section 2.1 "Converting to Monadic Style", a function is converted to return Value in the Eval1 monad. This part of the function doesn't make sense to me:
eval1 env (Var n) = Map.lookup n env
The result of that will be Maybe Value however the function's type signature is:
eval1 :: Env → Exp → Eval1 Value
The function is failing to type check, and the error seems obvious to me. Yet the author specifically states that this will work:
... the Var case does not need a fromJust call anymore: The reason is that Map.lookup is defined to work within any monad by simply calling the monad’s fail function – this fits nicely with our monadic formulation here.
The signature for Map.lookup does not look like it is designed to work with any monad:
lookup :: Ord k => k -> Map k a -> Maybe a
Is this paper out of date or am I missing something? If the paper is in fact out of date, why was lookup changed to only work with Maybe.
Thanks!
Your tutorial is from 2006. It uses a very old version of Data.Map in which lookup's type indeed was:
lookup :: (Monad m, Ord k) => k -> Map k a -> m a
I reckon the change happened because fail is widely considered to be a wart in the Monad class. Returning a Maybe a makes a lookup failure explicit and manageable. Making it implicit by hiding it behind fail just to have a slightly more convenient type is quite dirty IMO. (See also the question linked to by Ørjan.)
You can use this adapted version of lookup to follow along the tutorial:
fallibleLookup :: (Ord k, Monad m) => k -> Map.Map k a -> m a
fallibleLookup k = maybe (fail "fallibleLookup: Key not found") pure . Map.lookup k
Note that with the upcoming release of GHC 8.8 the proper constraint to use on m will be MonadFail rather than Monad.
From the example of Validation (https://hackage.haskell.org/package/Validation), I'm trying to get an intuition of detecting how/why an applicative could not be a Monad (Why can AccValidation not have a Monad instance?)
Could you challenge my reasoning ?
I think about a monad in the way we handle behind the join (m ( m b) -> m b), let's develop my understanding with an example like Validation:
in data Validation err a, the functor structure is (Validation err). When you look at the definition of the bind for Monad and specializing the types for Validation you get the following :
(>>=) :: m a -> (a -> m b) -> m b
(>>=) :: (Validation err) a -> ( a -> (Validation err) b) -> (Validation err) b
if you beta reduce (>>=) you'll get :
m a -> (a -> m b) -> m b // if we apply (m a) in the monadic function
m ( m b) -> m b
then to get the result of (>>=) which is m b, you'll use join :
join :: (Monad m) => m (m a) -> m a
join x = x >>= id
If you play with the types you'll get :
join m ( m b ) = m ( m b) >>= (\(m b) -> m b -> m b) which gives m b
So that join just drop the outermost structure, only the value in the innermost type (the value of the innermost functor) is kept/transmitted through the sequence.
In a monad we can't pass some information from the functor structure (e.g Validation err) to the next 'action', the only think we can pass is the value. The only think you could do with that structure is short-circuiting the sequence to get information from it.
You can't perform a sequence of action on the information from the functor structure (e.g accumulating something like error..)
So I would say that an applicative that is squashing its structure with some logic on its structure could be suspicious as not being able to become a Monad ?
This isn't really an answer, but it's too long for a comment.
This and other referenced discussions in that thread are relevant. I think the question is posed sort of backwards: all Monads naturally give rise to an Applicative (where pure = return, etc); the issue is that most users expect/assume that (where a type is instance Monad) the Applicative instance is semantically equivalent to the instance to which the Monad gives rise.
This is documented in the Applicative class as a sort of law, but I'm not totally convinced it's justified. The argument seems to be that having an Applicative and Monad that don't agree in this way is confusing.
My experience using Validation is that it's a nightmare to do anything large with it, both because the notation becomes a mess and because you find you have some data dependencies (e.g. you need to parse and validate one section based on the parse of a previous section). You end up defining bindV which behave like an Error monad >>= since a proper Monad instance is considered dubious.
And yet using a Monad/Applicative pair like this does what you want: especially when using ApplicativeDo (I imagine; haven't tried this), the effect of writing your parser (e.g.) in Monadic style is that you can accumulate as many errors as possible at every level, based on the data dependencies of your parsing code. Haxl arguably fudges this "law" in a similar way.
I don't have enough experience with other types that are Applicative but not Monad to know whether there's a sensible rule for when it's "okay" for the Applicative to disagree in this way. Maybe it's totally arbitrary that Validation seems to work sensibly.
In any case...
I'm not sure how to directly answer your question. I think you start by taking the laws documented at the bottom of Applicative class docs, and flip them, so you get:
return = pure
ap m1 m2 = m1 <*> m2
If ap were a method of Monad and the above was a minimal complete definition then you'd simply have to test whether the above passed the Monad laws to answer your question for any Applicative, but that's not the case of course.
If I have a function like, in a monad T, f1 :: T String, and I want to use its outcome, print it, for example.
seeF1 :: IO String
seeF1 = do
res <- f1
print res
Why is it wrong?. It seems that I can't use f1 because it is not in the monad IO. So, How can I do it? lifting?
In do notation, when you do
x = do
y <- z
....
Then if x :: (Monad m) => m a, then z :: (Monad m) => m b were m is the same monad.
That is pretty logical after all : imagine if your T monad was list, what should your seeF1 return? Or if your T monad was Maybe, seeF1 wouldn't be able to print anything in case it encountered a Nothing since the result would be undefined.
Therefore in general, what you are asking for is not possible. But if you are a bit more specific about your T, then you might find a way to get an IO a from your T a. For instance if you look at the monads defined in transformers, many have a run function that transform them, and from which you can get an IO.
I am going though the following paper: Monad Transformers Step by Step. In section 2.1 "Converting to Monadic Style", a function is converted to return Value in the Eval1 monad. This part of the function doesn't make sense to me:
eval1 env (Var n) = Map.lookup n env
The result of that will be Maybe Value however the function's type signature is:
eval1 :: Env → Exp → Eval1 Value
The function is failing to type check, and the error seems obvious to me. Yet the author specifically states that this will work:
... the Var case does not need a fromJust call anymore: The reason is that Map.lookup is defined to work within any monad by simply calling the monad’s fail function – this fits nicely with our monadic formulation here.
The signature for Map.lookup does not look like it is designed to work with any monad:
lookup :: Ord k => k -> Map k a -> Maybe a
Is this paper out of date or am I missing something? If the paper is in fact out of date, why was lookup changed to only work with Maybe.
Thanks!
Your tutorial is from 2006. It uses a very old version of Data.Map in which lookup's type indeed was:
lookup :: (Monad m, Ord k) => k -> Map k a -> m a
I reckon the change happened because fail is widely considered to be a wart in the Monad class. Returning a Maybe a makes a lookup failure explicit and manageable. Making it implicit by hiding it behind fail just to have a slightly more convenient type is quite dirty IMO. (See also the question linked to by Ørjan.)
You can use this adapted version of lookup to follow along the tutorial:
fallibleLookup :: (Ord k, Monad m) => k -> Map.Map k a -> m a
fallibleLookup k = maybe (fail "fallibleLookup: Key not found") pure . Map.lookup k
Note that with the upcoming release of GHC 8.8 the proper constraint to use on m will be MonadFail rather than Monad.
Lets say I have function
(>>*=) :: (Show e') => Either e' a -> (a -> Either e b) -> Either e b
which is converting errors of different types in clean streamlined functions. I am pretty happy about this.
BUT
Could there possibly be function <*- that would do similar job insted of <- keyword, that it would not look too disturbing?
Well, my answer is really the same as Toxaris' suggestion of a foo :: Either e a -> Either e' a function, but I'll try to motivate it a bit more.
A function like foo is what we call a monad morphism: a natural transformation from one monad into another one. You can informally think of this as a function that sends any action in the source monad (irrespective of result type) to a "sensible" counterpart in the target monad. (The "sensible" bit is where it gets mathy, so I'll skip those details...)
Monad morphisms are a more fundamental concept here than your suggested >>*= function for handling this sort of situation in Haskell. Your >>*= is well-behaved if it's equivalent to the following:
(>>*=) :: Monad m => n a -> (a -> m b) -> m b
na >>*= k = morph na >>= k
where
-- Must be a monad morphism:
morph :: n a -> m a
morph = ...
So it's best to factor your >>*= out into >>= and case-specific monad morphisms. If you read the link from above, and the tutorial for the mmorph library, you'll see examples of generic utility functions that use user-supplied monad morphisms to "edit" monad transformer stacks—for example, use a monad morphism morph :: Error e a -> Error e' a to convert StateT s (ErrorT e IO) a into StateT s (ErrorT e' IO) a.
It is not possible to write a function that you can use instead of the <- in do notation. The reason is that to the left of <-, there is a pattern, but functions take values. But maybe you can write a function
foo :: (Show e') => Either e' a -> Either e a
that converts the error messages and then use it like this:
do x <- foo $ code that creates e1 errors
y <- foo $ code that creates e2 errors
While this is not as good as the <*- you're asking for, it should allow you to use do notation.