Monad transformers question

Derek Elkins ddarius@hotpop.com
Sun, 4 May 2003 15:08:20 -0400


On Sun, 04 May 2003 20:06:32 +0200
Magnus Lindberg <f98mali@dd.chalmers.se> wrote:

> {-  Hi !
> I have tried to write an "EnvState" monad transformer, but it is quite
> useless because when a function peeks at the state but ignores the
> environment (or vice versa) the code won't compile. GHC tells that the
> monad I use is no instance of the MonadEnvState, but I really think it
> is!
>   An example:
> ( I attach the monad transformer file, MonadTrans2.hs, if someone
> wonders how EnvStateT looks (I have only written EnvStateT myself, my
> teacher has written the others some time ago (I think))  )
> -}
> 
> 
> import MonadTrans2
> 
> 
> data Env   = Env Int
> data State = State Int
> 
> 
> type M = EnvStateT Env State IO
> 
> f, g, h :: M ()
> 
> 
> f = do (Env e, State s) <- getES    -- This works since I show GHC
>        return ()                    --   exactly what Env and State I
> use ?
> 
> g = do State s <- getS          -- compile time error!
>        return ()
> 
> h = do Env e <- getE            -- compile time error!
>        return ()
> 
> 
> {-
> I have already told Haskell that the monad M uses an `Env' and a
> `State' (the `type M = ...') so why does GHC complain? The function
> `g' gives:
> 
>     No instance for (MonadEnvState (EnvStateT Env State IO) e State)
>     arising from use of `getS' at envstate.hs:33
>     In a 'do' expression pattern binding: State x <- getS
>     In the definition of `g':
>         do
>           State x <- getS
>           return ()
>           
> This means that my environment-state monad is nearly useless, since
> all functions have to use both the state and the environment in order
> to compile. I thought the following could solve the problem:
> 
> instance MonadEnvState M Env State
> 
> since I then tell Haskell that M is an instance of MonadEnvState, but
> then the code won't compile: 
>     
>     Illegal instance declaration for `MonadEnvState M Env State'
>         (The instance type must be of form (T a b c)
>          where T is not a synonym, and a,b,c are distinct type 
>          variables)
>     In the instance declaration for `MonadEnvState M Env State'
>     
> Does anyone know what to do? Is this a problem with Haskell without
> any solution? Must one perhaps first make a state monad and then
> transform the state monad into an environment monad? Can't one have
> both a state and an environment in one single monad at the same time
> without these compiler errors?
> 
> Thanks for any help !
> 
> Best regards, Magnus Lindberg
> -}

If your instructor gave you the task of writing a monad transformer with
environment and state, and (s)he provided you with MonadTrans2.hs, it
seems to me that you are meant to use the declarations in it, rather
than ignore them and write something from scratch.  If the directions
were specifically to implement this from scratch, or you weren't given
MonadTrans2.hs as part of the exercise, or you are doing this on your
own, then you'll need to learn some of the details of the non-standard
extension, multiparameter type classes.

Your analysis of why getES works is accurate.  Your analysis of the use
of your monad isn't completely accurate.  Your monad is cumbersome, not
useless.  As it is now, you have to directly or indirectly specify the
type for getES (you'll get a similar error with (Env _,_) <- getES, I'm
fairly certain), and you have to directly specify the type for getS/E. 
So, to solve your problem, you can explicitly specify the type of
getS/E/ES everytime you use it.  It can't be that hard, most statically
typed language programmers have been putting up with that for about half
a century :D.

Okay, that solution is far from ideal.  The real solution is functional
dependencies.  Rather than give you the answer on a silver plate, I'll
leave it to you to look up the relevant information and figure out how
to apply it to your problem.  If you still can't figure it out, then
post again, though I expect the timestamp to be at least 18 hours after
the timestamp on this message.