0

Let's say I have a computation

class A m where
 foo :: m () -> m ()
instance A IO where
 foo x = do
 print "prefix"
 x
 print "suffix"

Now, suppose I want to write

instance A m => A (MyMonadTransformerT γ m)

Then, in implementing foo, I'm forced to "unwrap" its argument, e.g. foo x = lift (foo (unlift x)). This unlift function can be bad for monadic computation. For a state transformer, it would be forced to forget any changes in the program's state.

It seems to work to create a more general method, which also takes a lifting function, and results in computation t () -> t (), where t is the lifted (tranformed) monad.

class Monad m => A' m where
 foo' :: Monad t =>
 (forall z . m z -> t z) -- lifting function
 -> t ()
 -> t ()
 foo :: m () -> m ()
 foo = foo' id
instance A' IO where
 foo' lift x = do
 lift (print "prefix")
 x
 lift (print "suffix")
instance A' m => A' (StateT γ m) where
 foo' lift' x = foo' (lift' . lift) x
computation :: Num a => StateT a IO ()
computation = do
 foo (put 1 >> lift (print "middle"))
 v <- get
 lift $ print ("value", v)
run_computation :: Num a => IO a
run_computation = execStateT computation 0

Question. Is this the best way? Is there something more general one can write? CPS-style code? Thanks!!

asked Feb 15, 2012 at 22:53
2
  • 2
    possible duplicate of Lifting a higher order function in Haskell Commented Feb 16, 2012 at 0:28
  • Yes, I think it's a duplicate. The "refying the class" approach I and luqui took seems to be the nicest though. Commented Feb 21, 2012 at 18:38

1 Answer 1

2

First of all, forget that class business, it looks like you just want a function.

This problem is addressed by the Monad* classes: MonadIO, MonadState, etc. So if you have a monadic computation which can do IO, but which is allowed to do other things, you would take as a type parameter m any monad which can perform IO actions:

foo :: (MonadIO m) => m () -> m ()
foo x = do
 liftIO $ putStrLn "prefix"
 x
 liftIO $ putStrLn "suffix"

Now it does not matter what m is, because MonadIO says how to lift it back to the operations you want.

The Monad* classes are somewhat non-modular in the face of new transformers -- the number of instances you need is quadratic in the number of monad transformers. There are various suboptimal solutions to this problem. If such things concern you, you can always reify the class:

foo :: (Monad m) => (forall a. IO a -> m a) -> m () -> m ()
foo lift x = do
 lift $ putStrLn "prefix"
 x
 lift $ putStrLn "suffix"

Whether to do this depends on your level of abstraction. You will want the former if you are writing a library upon which to build content code, and perhaps the latter if you are writing a library upon which to build other library code. It's kind of tricky either way though, all because monad stacks don't commute.

answered Feb 16, 2012 at 1:40
Sign up to request clarification or add additional context in comments.

1 Comment

I haven't shown other instances of the class. So, yes, I'm sure, I want a class, but only for foo', foo should be a function. Besides, the compiler won't be able to infer whether to use lift (1 monad transformer), lift . lift (2 monad transformers), etc. by itself.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.