Come fare un interprete per una pila di Transformers Monad gratuiti

2

Mi piace l'idea di usare le monade libere per "purificare" il codice e l'ho usato in alcuni semplici scenari. Tuttavia, non riesco a capire come scrivere un interprete per un trasformatore gratuito. In particolare una pila di monadi gratuiti.

Ecco un semplice esempio di ciò che ho in mente. Diciamo che sto depurando l'accesso a due database. Posso scrivere

{-# 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

che funziona bene per l'utilizzo di DB1 e DB2 separatamente. Ma se voglio usarli insieme, penserei di impilare un trasformatore con l'altro come

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

Ma posso capire come scrivere una di queste funzioni:

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

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

Anche io non riesco a trovare esempi. Un monade stack gratuito è un brutto modo di fare questo genere di cose?

    
posta Lorne 23.01.2015 - 03:54
fonte

1 risposta

0

Sulla base dell'esempio che Daniel ha fornito nei commenti, ecco una risposta nel caso in cui qualcuno dovesse inciampare qui:

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

con {-# LANGUAGE RankNTypes #-} .

    
risposta data 24.01.2015 - 02:20
fonte

Leggi altre domande sui tag