shang
shang

Reputation: 24822

Is there a library or typeclass for getting the transformer version of a monad?

In my current project I've run into the need to turn various monads into their transformer counterparts e.g.

stateT :: Monad m => State s a -> StateT s m a
stateT stf = StateT $ return . runState stf

It's trivial to write these utility functions for the monads I need, but I was wondering if there already exists a library that contains this functionality for the standard monads and maybe a typeclass that abstracts this sort of transformation. Something like

class (Monad f, MonadTrans t) => LiftTrans f t | f -> t where
    liftT :: Monad m => f a -> t m a

("lift" is probably the wrong term to use here, but I wasn't sure what else to call it.)

Upvotes: 11

Views: 361

Answers (2)

Boyd Stephen Smith Jr.
Boyd Stephen Smith Jr.

Reputation: 3202

You are trying to go the wrong way. If something is a transformer, than the plain version of it is the transformer applied to the Identity monad. (They are not always implemented like that directly, but should be equivalent modulo bottom.) But, not all monads are transformers (They need to be universally left or universally right adjoint in order to transform. [citation needed])

hoist and friends are super helpful, too, but I think it best to invert your process.

Upvotes: 0

danidiaz
danidiaz

Reputation: 27766

Check out function hoist from the mmorph package.

Its signature is

hoist :: Monad m => (forall a. m a -> n a) -> t m b -> t n b

Meaning that it can change the base monad underlying a transformer.

Now, in the trasformers package, many "basic" monads are implemented as transformers applied to the Identity monad, like this:

type State s = StateT s Identity

Therefore, we can define the following function (taken form the Generalizing base monads section of the mmorph documentation):

import Data.Functor.Identity

generalize :: (Monad m) => Identity a -> m a
generalize m = return (runIdentity m)

and combine it with hoist:

hoist generalize :: (Monad m, MFunctor t) => t Identity b -> t m b

This method won't work for simple monads which are not defined as transformers applied to Identity, like the Maybe and Either monads. You are stuck with hoistMaybe and hoistEither for these.

Upvotes: 17

Related Questions