
I had an interaction on #haskell today which left me utterly confused but desiring to understand what happened. I was talking about a program in which I was using stacks of monad transformers, inspiration taken from the paper "Monad Transformers: Step by Step". So for example I wanted to combine state and error handling in a Monadic type called "Er". data ErState = ErState StdGen newtype Er a = Er { runEr :: ErrorT String (State ErState) a } deriving(Monad, MonadError String, MonadState ErState) Note that ErState holds a random generator. I had the idea to make a typeclass of monadic types that hold random generators because I'll be using them in other types all over my application. Something like class Monad m => RandomMonad m where getGen :: m StdGen putGen :: StdGen -> m () Then I can write functions like this: myRandoms :: (Random a, RandomMonad m) => m [a] myRandoms = do g <- getGen let (g1, g2) = split g values = randoms g1 putGen g2 return values So then mm_freak on #haskell noticed that I had written a function with a signature like process :: Int -> Er Int He said, no no no functions should be agnostic with regard to data structure. It should look something like process :: (RandomMonad m, MonadError m, MonadState m) => Int -> m Int He then said I don't want to put StateT and ErrorT in the same newtype declaration, so it would be something like newtype Er m a = Er { runEr :: StateT ErState m a } On this point I'm confused. I don't know how to get from a line like the above to eventually making a type that combines error handling and state. Furthermore I need to define instances for things like MonadError. He got me started with the line instance MonadError e m => MonadError e (Er m) where ... define throwError and catchError which I have no idea how to do ... So where we left it, I was confused and didn't understand anything. Rather than just getting the answers, I want to try to understand this, but I feel like I must need more background. This must be the kind of code that is in the monad transformer library. Is there a guide to understanding that somewhere? D

On Sat, Mar 29, 2014, at 03:32 AM, Dennis Raddle wrote: He then said I don't want to put StateT and ErrorT in the same newtype declaration, so it would be something like newtype Er m a = Er { runEr :: StateT ErState m a } On this point I'm confused. I don't know how to get from a line like the above to eventually making a type that combines error handling and state. Furthermore I need to define instances for things like MonadError. I don't think there was really anything wrong with your prior approach. It seems like the advice you got was to make the Er type more generic, which may or may not be appropriate in the context of your application. At this point, Er is now a monad transformer. It's not really a monad until the "m" type parameter is instantiated. For example: type ErActuallyAMonad a = Er Error a Notice that the value inside the newtype will then be a "StateT ErState Error a", which is almost the same as the "ErrorT (State ErState) a" you had originally. He got me started with the line instance MonadError e m => MonadError e (Er m) where ... define throwError and catchError which I have no idea how to do ... An instance declaration like this is saying that if you have a MonadError monad m, and you transform it with the transformer Er, then the resulting monad will also be a MonadError monad. These instance declarations are pretty common in the MTL. -Karl
participants (2)
-
Dennis Raddle
-
Karl Voelker