2011-12-05 16 views
15

Uno dei problemi con i trasformatori monad che trovo è la necessità di lift le operazioni nella monade destra. Una singola lift qui e non là è male, ma a volte ci sono funzioni che assomiglia a questo:Lavorare su stack di trasformatori monad permutati

fun = do 
    lift a 
    lift b 
    c 
    lift d 
    lift e 
    f 

Mi piacerebbe essere in grado di scrivere questa funzione così:

fun = monadInvert $ do 
    a 
    b 
    lift c 
    d 
    e 
    lift f 

Questo dimezza il numero di lift s e rende il codice più pulito.

La domanda è: per quale monade è possibile monadInvert? Come si dovrebbe creare questa funzione?

Punti bonus: definirlo per monad m che è un'istanza di MonadIO.

Il titolo di questa domanda parla di permutazioni: in effetti, come possiamo gestire le permutazioni arbitrarie di uno stack monod tranformer?

+0

Dubito fortemente che tu possa (ma forse qualche malvagio trucco può essere usato). Pensare solo ai tipi non mi dà peso. –

+0

Non proprio una soluzione pronta per la pala, ma potresti trovare [questo documento] (http://www.cs.umd.edu/~mwh/papers/monadic.pdf) che vale la pena leggere; dall'astratto: "il nostro algoritmo inserisce i vincoli, le unità e i morfismi monad-to-monad necessari in modo che il tipo di programma controlli" – acfoltzer

risposta

10

Potresti essere interessato a Monads, Zippers and Views, Virtualizing the Monad Stack di Tom Schrijvers e Bruno Oliveira.

Questo non risolve il problema relativo alla riduzione degli ascensori, ma è un approccio interessante al tuo problema di "permutazione monade".

Ecco il riassunto:

Questo lavoro mira a rendere i componenti monadici più riutilizzabile e robusto ai cambiamenti impiegando due nuove tecniche per la virtualizzazione dello stack Monade : la cerniera Monade e Monade vista. Il monad zipper è un trasformatore monad che crea stack virtuali monad ignorando determinati livelli in uno stack di cemento. Le viste Monad forniscono una struttura generale per la virtualizzazione dello stack monad: prendono la monad zipper un passo e la integrano con una vasta gamma di altre virtualizzazioni. Ad esempio, viste particolari consentono l'accesso limitato alle monadi nello stack . Inoltre, le viste monad possono essere utilizzate dai componenti per fornire un meccanismo call-by-reference-like per accedere a particolari layer dello stack monad. Con questi due meccanismi, i requisiti dei componenti in termini di forma dello stack monad non devono più essere letteralmente riflessi nello stack di monad concreto, rendendo questi componenti più riutilizzabili e affidabili alle modifiche.

16

Bene, prima di tutto, in realtà non è necessario un tale sollevamento. Per i trasformatori monade le seguenti identità tengono:

lift c >>= lift . f = lift (c >>= f) 
lift c1 >> lift c2 = lift (c1 >> c2) 

non è raro a scrivere:

x <- lift $ do 
    {- ... -} 

successivo è: Quando si utilizzano le librerie come mtl o monadLib (vale a direlibrerie basate tipo di classe invece di trasformatori direttamente), in realtà si può accedere alla maggior parte monadi sottostanti direttamente:

c :: StateT MyState (ReaderT MyConfig SomeOtherMonad) Result 
c = do 
    x <- ask 
    y <- get 
    {- ... -} 

Infine, se si ha realmente bisogno di un sacco di sollevamento, nonostante questi due punti, si dovrebbe non crei una monade personalizzato o addirittura usare un'astrazione completamente diversa. Mi trovo ad usare la freccia dell'automa per calcoli con stato invece di una monade di stato.

+1

Non proprio una soluzione, ma grazie comunque. Nota che non posso scrivere 'lift (a >> b >> c)', perché a, b, c provengono da due diverse monadi. – Tener

1

Sono per lo più in modo che ciò che si sta decribing è impossibile per IO, che è sempre la monade più interno:

Da Martin Grabmüller: Monade Transformers Step by Step, disponibile presso http://www.grabmueller.de/martin/www/pub/

di Down in questo documento, chiamiamo liftIO in eval6 per eseguire azioni I/O. Perché dobbiamo sollevare in questo caso? Perché non esiste una classe IO per che possiamo istanziare un tipo come. Pertanto, per le azioni di I/O, abbiamo dobbiamo chiamare ascensore per inviare i comandi verso l'interno

In generale, per monadi meno restrittive rispetto IO, (come ad esempio errori e Stato) ordine è ancora importante per la semantica, in modo da non è possibile modificare l'ordine dello stack solo per semplificare la sintassi.

Per alcune monadi, come Reader, che sono centrali (nel senso che fanno il pendolare nello stack), la tua idea non sembra chiaramente impossibile. Non so come scriverlo comunque. Immagino che sarebbe una classe di tipo CentralMonad che ReaderT è un'istanza di, con qualche implementazione ...

Problemi correlati