2

I like the idea of using Free monads to "purify" code and I've used it in some simple scenarios. However, I can't seem to figure out how to write an interpreter for a Free monad transformer. In particular a stack of free monads.

Here's a simple example of what I have in mind. Say I'm purifying access to two databases. I can write

{-# LANGUAGE DeriveFunctor #-}
module Question where

import Control.Monad.Trans.Free
import Control.Applicative ( (<$>) )
import Control.Monad.Trans.Class ( lift )

data DB1reqF x = FetchName String ( String -> x ) deriving (Functor)

data DB2reqF x = FetchInt String ( Int -> x ) deriving (Functor)

type DB1req = Free DB1reqF
type DB2req = Free DB2reqF
type DB2reqT = FreeT DB2reqF

fetchName :: String -> DB1req String
fetchName s = liftF $ FetchName s id

fetchInt :: String -> DB2req Int
fetchInt s = liftF $ FetchInt s id

runDB1reqF :: DB1reqF ( IO a ) -> IO a
runDB1reqF ( FetchName s n ) = putStrLn ( "Get from DB1: " ++ s  ) >> getLine >>= n

runDB2reqF :: DB2reqF ( IO a ) -> IO a
runDB2reqF ( FetchInt s n ) = putStrLn ( "Get from DB2: " ++ s  ) >> read <$> getLine >>= n

runDB1 :: DB1req a -> IO a
runDB1 = iterM runDB1reqF

runDB2 :: DB2req a -> IO a
runDB2 = iterM runDB2reqF

which works fine for using DB1 and DB2 separately. But if I want to use them together, I would think to stack a transformer with the other like

type CompoundProg = DB2reqT DB1req

fetchIntT :: (Monad m) => String -> DB2reqT m Int
fetchIntT s  = liftF $ FetchInt s id

complexProg :: CompoundProg Int
complexProg = do
    n <- lift $ fetchName "Fred"
    fetchIntT n

But I can figure out how to write either of these functions:

runCompound :: CompoundProg a -> IO a
runCompound = undefined

runDB2T :: ( Monad m ) => DB2reqT m a -> m ( IO a )
runDB2T = undefined

I also can't seem to find any examples. Is a free monad stack a bad way to go about doing this sort of thing?

0

Based on the example Daniel gave in the comments, here's an answer in case anyone stumbles here:

runDB2T :: ( Monad m ) => ( forall b. m b -> IO b ) -> DB2reqT m a ->  IO a
runDB2T f = iterT runDB2reqF . hoistFreeT f

runCompound :: CompoundProg a -> IO a
runCompound = runDB2T runDB1

with {-# LANGUAGE RankNTypes #-}.

|improve this answer|||||

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.