2012-02-09 21 views
6

Attualmente sto scrivendo un progetto in cui faccio un uso massiccio del trasformatore monad ListT. Quando si usano liste semplici, implementare il nondeterminismo è molto semplice. Tuttavia, una volta che ho dovuto convertire il mio codice in ListT, è diventato molto più complicato .Come convertire in modo pulito tra liste e trasformatori monad ListT?

Come semplice esempio: la conversione [a]-ListT a in realtà richiede la composizione di due funzioni:

conv :: (Monad m) => [a] -> ListT m a 
conv = ListT . return 

Anche se è semplice, sono sorpreso che non è già lì.

Domande:

  • C'è qualche modo migliore per gestire nondeterminismo in cui è necessario un trasformatore monade?
  • Esistono tecniche/librerie per convertire in modo pulito avanti e indietro tra gli elenchi e ListT?

I motivi esatti sono piuttosto complicato, quindi non ho davvero voglio dilungarmi troppo su questo.

risposta

6

Non penso ci siano librerie per questo; conv è una funzione incredibilmente semplice, dopotutto, e il contrario è solo runListT.

conv è simile al liftMaybe spesso desiderato quando si utilizza MaybeT:

liftMaybe :: (Monad m) => Maybe a -> MaybeT m a 
liftMaybe = MaybeT . return 

lo consiglio chiamandolo qualcosa lungo le linee di liftList.

quanto riguarda una migliore trasformatore monade per nondeterminismo va, vi consiglio di dare un'occhiata al pacchetto logict, basato su LogicT trasformatore di Oleg, che è una monade logica backtracking continuazione-based con some helpful operations. Come bonus, dal momento che [] è un'istanza di MonadLogic, tali operazioni funzionano anche sugli elenchi.


È interessante notare che possiamo definire una funzione che generalizza il modello di conv e liftMaybe:

import Data.Foldable (Foldable) 
import qualified Data.Foldable as F 

choose :: (Foldable t, MonadPlus m) => t a -> m a 
choose = F.foldr (\a b -> return a `mplus` b) mzero 

Questo sarà probabilmente rendere il codice molto confusa, in modo da non consiglia di utilizzare lo :)

+0

Sì, sono d'accordo che 'conv' è una funzione semplice. Sono solo sorpreso che non sia già lì. Ci sono pochissime utilità nel modulo 'ListT' che mi fa sentire come se reinventassi la ruota. È tutto. – julkiewicz

0

Mi sono imbattuto in questa domanda pochi mesi dopo perché mi chiedevo qualcosa di simile a questo. Così mi è venuta con il seguente:

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-} 

import Control.Monad.Trans.Class 
import Control.Monad.Trans.Maybe 
import Control.Monad.Trans.List 


-- | Minimal implementation: either joinLift or joinT 
class (MonadTrans t, Monad m) => MonadTransJoin t m | m -> t, t -> m where 
    joinLift :: (Monad m', Monad (t m')) => m' (m a) -> t m' a 
    joinLift = joinT . lift 

    joinT :: (Monad m', Monad (t m')) => t m' (m a) -> t m' a 
    joinT = (>>= (joinLift . return)) 


instance MonadTransJoin MaybeT Maybe where 
    joinLift = MaybeT 
    joinT = (>>= maybe mzero return) 

instance MonadTransJoin ListT [] where 
    joinLift = ListT 
    joinT = (>>= foldr mcons mzero) 
     where mcons x xs = return x `mplus` xs 

Fin qui tutto bene, e il mio metodo joinT per la coppia ListT/[] sembra che abbia qualcosa a che fare con ehird di choose.

Ma il problema con questo è che in realtà non esiste un'interfaccia uniforme tra un trasformatore monade e la monade il cui comportamento conferisce alla sua monade di base. Abbiamo MaybeT :: m (Maybe a) -> MaybeT m a e ListT :: m [a] -> ListT m a, ma OTOH abbiamo StateT :: (s -> m (a, s)) -> StateT s m a. Non so se c'è un modo per aggirare questo problema - richiede sicuramente