views:

155

answers:

2

Hello,

I'm trying to create a monad transformer for a future project, but unfortunately, my implementation of the Monad typeclasse's (>>=) function doesn't work.

First of all, here is the underlying monad's implementation :

newtype Runtime a = R { 
  unR :: State EInfo a
} deriving (Monad)

Here, the implementation of the Monad typeclasse is done automatically by GHC (using the GeneralizedNewtypeDeriving language pragma). The monad transformer is defined as so :

newtype RuntimeT m a = RuntimeT {
  runRuntimeT :: m (Runtime a)
} 

The problem comes from the way I instanciate the (>>=) function of the Monad typeclasse :

instance (Monad m) => Monad (RuntimeT m) where
    return a = RuntimeT $ (return . return) a
    x >>= f =  runRuntimeT x >>= id >>= f

The way I see it, the first >>= runs in the underlying m monad. Thus, runRuntimeT x >>= returns a value of type Runtime a (right ?). Then, the following code, id >>=, should return a value of type a. This value is the passed on to the function f of type f :: (Monad m) => a -> RuntimeT m b.

And here comes the type problem : the f function's type doesn't match the type required by the (>>=) function. Jow can I make this coherent ? I can see why this doesn't work, but I can't manage to turn it into something functionnal.

Edit : The error message :

Core.hs:34:4:
    Occurs check: cannot construct the infinite type: m = RuntimeT m
    When generalising the type(s) for `>>='
    In the instance declaration for `Monad (RuntimeT m)'
Failed, modules loaded: none.

Thank you for you help, and do not hesitate to correct any flaws in my message,
Charlie P.

+4  A: 
Reid Barton
Imagine I have a function running inside the Runtime monad. This monad is totally opaque, and the user is not supposed to know that it is in fact a State monad with sugar on top. The idea behind the RuntimeT monad transformer it to be able to use functions running in the Runtime monad transparently with, let's say, functions running in the IO monad. This is why I can't use the StateT monad transfomer instead of my custom one.
CharlieP
Thanks for pointing out the illogic part of my monad, I am going to use the approach you and Alasdair stated, combined with the identity monad.
CharlieP
+2  A: 

To follow on from what Reid Barton said about StateT - here's how you would define RuntimeT using StateT. The Runtime monad can then be trivially defined using the identity monad.

newtype RuntimeT m a = R { 
  unR :: StateT EInfo m a
}

type Runtime = RuntimeT Identity

instance Monad m => Monad (RuntimeT m) where
    return = R . return

    (R m) >>= f = R (m >>= unR . f)
Alasdair
I had no idea the Identity monad existed, and it looks like it's what I'm trying to get at. However, as I said while commenting Reid Barton's post, the idea behind creating this new monad and monad transformer is to hide the State monad from the user. How can I achieve this using what you stated in your message ?
CharlieP
@CharlieP: The problem is that StateT sandwiches the underlying monad in between the s -> and (a, s), while your attempted transformer failed to do that. If you prefer, you can choose to derive Monad (or even MonadState EInfo) for the newtype that Alasdair provided as well, but without using StateT or hand coding its equivalent you won't be able to use your monad as a transformer.Expanding the definition of StateT yields newtype RuntimeT m a = RuntimeT { unRuntimeT :: EInfo -> m (a, EInfo) } but your definition yields newtype RuntimeT m a = RuntimeT { unRuntimeT :: m (EInfo -> (a, EInfo)) }
Edward Kmett
Charlie, you'd typically use Haskell's module system to hide the implementation details of `RuntimeT`. Provided you don't export `unR` or `R` the user need never know you used `StateT`.For example, you'll probably want a `runStateT` equivalent for `RuntimeT` - that can be implemented as `runRuntimeT = runStateT . unR`. Note it's type `RuntimeT m a -> EInfo -> m (a, EInfo)`. `StateT` doesn't appear anywhere, and somebody using this function can use it while being blissfully unaware of `StateT`'s existence. Similar functions can be written for get/put. Hope that helps.
Alasdair
Thanks for the help guys, this problem is now solved.
CharlieP