Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Updating outer monad only in monad transformer

I have a monad for a computation that may fail and does some logging:

f1 :: WriterT [String] (Either String) a

I have a function that will not fail but does some logging:

f2 :: Writer [String] b

What's the best way to update the writer monad in f1 using the log from f2, and capture the output of the f2 computation? At the moment I'm doing this:

f2result <- (\(r,l) -> do {tell l; return r}) (runWriter f2)

I am using lift to update the inner monad with a different computation, so switching around the Writer and Either monads will not solve the problem.

like image 332
mskel Avatar asked Mar 14 '12 13:03

mskel


People also ask

What is monad transformer in C++?

Monad transformer. In functional programming, a monad transformer is a type constructor which takes a monad as an argument and returns a monad as a result. Monad transformers can be used to compose features encapsulated by monads – such as state, exception handling, and I/O – in a modular way.

What is the use of lift in monad transformer?

In a monad transformer, the lift function allows you to run actions in the underlying monad. This behavior is encompassed by the MonadTrans class: So using lift in the ReaderT Env IO action allows IO functions. Using the type template from the class, we can substitute Reader Env for t, and IO for m.

What is the difference between monad transformation and converse transformation?

Note that monad transformations are usually not commutative: for instance, applying the state transformer to the option monad yields a type (a computation which may fail and yield no final state), whereas the converse transformation has type (a computation which yields a final state and an optional return value).

Why do we use helper functions in Monad transformers?

Thus helper functions are frequently used for this. Additionally, since monad transformers can run several layers deep, the types can get complicated. So it is typical to use type synonyms liberally. As a similar idea, there are some typeclasses which allow you to make certain assumptions about the monad stack below.


1 Answers

If you defined f2, the easiest possible approach may be to refactor f2 so it's defined thusly:

 f2 :: Monad m => WriterT [String] m b

Which shouldn't be too hard, since Writer w b is defined as WriterT w Identity b, and the Identity monad doesn't give you anything.

Then you'd be able to chain them just by doing f1 >> f2.

If you can't redefine f2, you could always define your own with the appropriate signature:

 f2' :: Monad m => WriterT [String] m b
 f2' = WriterT . return $ runWriter f2

And if you've a bunch of f2 to wrap, you could always define a function to wrap them for you

 wrap :: Monad m => Writer w b -> WriterT w m b
 wrap = WriterT . return . runWriter

So you can do f1 >> wrap f2a >> wrap f2b >> wrap f2c ...

like image 65
rampion Avatar answered Oct 03 '22 14:10

rampion