Build my own monad transformer module hiding the main monad

I am learning mtl and I want to know the correct way to create new monads as modules (not as a typical application use).

As a simple example, I wrote ZipperT

monad here ( full code ):

{-# LANGUAGE FlexibleInstances, FunctionalDependencies, MultiParamTypeClasses, GeneralizedNewtypeDeriving #-}
module ZipperT (
  MonadZipper (..)
, ZipperT
, runZipperT
) where

import Control.Applicative
import Control.Monad.State

class Monad m => MonadZipper a m | m -> a where
    pushL :: a -> m ()
    pushR :: a -> m ()
    ...

data ZipperState s = ZipperState { left :: [s], right :: [s] }

newtype ZipperT s m a = ZipperT_ { runZipperT_ :: StateT (ZipperState s) m a }
                        deriving ( Functor, Applicative
                                 , Monad, MonadIO, MonadTrans
                                 , MonadState (ZipperState s))

instance (Monad m) => MonadZipper s (ZipperT s m) where
    pushL x = modify $ \(ZipperState left right) -> ZipperState (x:left) right
    pushR x = modify $ \(ZipperState left right) -> ZipperState left (x:right)
    ...

runZipperT :: (Monad m) => ZipperT s m a -> ([s], [s]) -> m (a, ([s], [s]))
runZipperT computation (left, right) = do
    (x, ZipperState left' right') <- runStateT (runZipperT_ computation) (ZipperState left right)
    return (x, (left', right'))

      

it works and i can compose with other monads

import Control.Monad.Identity
import Control.Monad.State
import ZipperT

length' :: [a] -> Int
length' xs = runIdentity (execStateT (runZipperT contar ([], xs)) 0)
    where contar = headR >>= \x -> case x of
                     Nothing -> return ()
                     Just  _ -> do
                                    right2left
                                    (lift . modify) (+1)
                                 -- ^^^^^^^
                                    contar

      

But I want to avoid being explicit lift

.

  • What is the correct way to create such modules?
  • Can you avoid the explicit lift

    ? (I want to hide the inner structure of StateT

    mine ZipperT

    )

Thank!

+3


source to share


1 answer


I think if you can write an instance MonadState

for your transformer, you can use modify

without lift

:

instance Monad m => MonadState (ZipperT s m a) where
   ...

      

I have to admit, I'm not sure which part of the condition modify

should be affected, though.




I have looked at the complete code. It seems that you have already identified

MonadState (ZipperState s) (ZipperT s m)

      

This already provides modify

, which, however, changes the wrong baseline. What you really wanted was to expose the state wrapped in m

, provided that it is MonadState

. In theory, this could be done with

instance MonadState s m => MonadState s (ZipperT s m) where
   ...

      

But now we have two instances MonadState

for the same monad, resulting in a conflict.




I guess I solved it somehow.

Here's what I did:

First, I deleted the original instance deriving MonadState

. Instead, I wrote

getZ :: Monad m => ZipperT s m (ZipperState s)
getZ = ZipperT_ get

putZ :: Monad m => ZipperState s -> ZipperT s m ()
putZ = ZipperT_ . put

modifyZ :: Monad m => (ZipperState s -> ZipperState s) -> ZipperT s m ()
modifyZ = ZipperT_ . modify

      

and replaced the previous entries get,put,modify

in the library ZipperT

with the above UDFs.

Then I added a new instance:

-- This requires UndecidableInstances
instance MonadState s m => MonadState s (ZipperT a m) where
   get = lift get
   put = lift . put

      

And now the client code works without elevators:

length' :: [a] -> Int
length' xs = runIdentity (execStateT (runZipperT contar ([], xs)) 0)
    where contar :: ZipperT a (StateT Int Identity) ()
          contar = headR >>= \x -> case x of
                     Nothing -> return ()
                     Just  _ -> do
                                    right2left
                                    modify (+ (1::Int))
                                 -- ^^^^^^^
                                    contar

      

+3


source







All Articles