2013-04-17 1 views
6

Ho il seguente codice:Forse Monade pila all'interno di trasformatori

import Control.Monad 
import Control.Monad.Trans 
import Control.Monad.Trans.State 

type T = StateT Int IO Int 

someMaybe = Just 3 

f :: T 
f = do 
    x <- get 
    val <- lift $ do 
     val <- someMaybe 
     -- more code in Maybe monad 
     -- return 4 
    return 3 

Quando uso do notazione all'interno di lavorare in Maybe monade fallisce. Dall'errore che assomiglia al tipo di firma per questo do non corrisponde. Tuttavia non ho idea di come risolverlo. Ho provato alcune combinazioni lift, ma nessuna ha funzionato e non voglio più indovinarlo.

+0

Vuoi il codice interno 'do' per eseguire solo nel Maybe' Monade', o ha bisogno di accedere al ' StateT Int' e 'IO' too? – pat

+0

Solo 'Forse' Monade. – Adrian

risposta

8

Il problema è che Maybe non fa parte dello stack del trasformatore. Se il tuo trasformatore conosce solo StateT Int e IO, non sa nulla su come sollevare Maybe.

È possibile risolvere questo problema modificando il tipo di T a qualcosa di simile:

type T = StateT Int (MaybeT IO) Int 

(Avrai bisogno di importare Control.Monad.Trans.Maybe.)

Sarà inoltre necessario cambiare il vostro interno do di lavorare con MaybeT anziché Maybe. Questo significa avvolgere prime Maybe a valori con MaybeT . return:

f :: T 
f = do 
    x <- get 
    val <- lift $ do 
     val <- MaybeT $ return someMaybe 
     -- more code in Maybe monad 
     return 4 
    return 3 

Questo è un po 'imbarazzante, quindi probabilmente si vuole scrivere una funzione come liftMaybe:

liftMaybe = MaybeT . return 

Se è stato utilizzato lift di sollevare IO a valori in altri parti del tuo codice, questo verrà interrotto perché hai ora tre livelli nella tua pila di trasformatori. Si otterrà un errore che assomiglia a questo:

Couldn't match expected type `MaybeT IO t0' 
      with actual type `IO String' 

Per risolvere questo problema, è necessario utilizzare liftIO per tutte le vostre prime IO a valori. Questo utilizza un typeclass per le azioni di vita IO attraverso un numero qualsiasi di livelli di trasformatore.

In risposta al tuo commento: se hai solo un po 'di codice a seconda Maybe, sarebbe più facile solo per mettere il risultato della notazione do in una variabile e partita contro che:

let maybeVal = do val <- someMaybe 
        -- more Maybe code 
        return 4 
case maybeVal of 
    Just res -> ... 
    Nothing -> ... 

Ciò significa che il codice Maybe non sarà in grado di eseguire un IO. È anche possibile utilizzare naturalmente una funzione come fromMaybe anziché case.

+0

Ho un sacco di funzioni che operano in questa monade, ma in una sola di esse devo decomprimere alcuni valori da 'Maybe' e usare la notazione' do' mi permetterebbe di eliminare l'espressione nidificata di 'caso'. Il tipo di definizione è l'unico modo per farlo? – Adrian

+0

Naturalmente è anche possibile usare 'runMaybeT' per il blocco che utilizza il trasformatore' MaybeT'. – dflemstr

+0

@Adrian: puoi semplicemente estrarre la notazione 'do' nella sua funzione e poi avere una singola espressione caso corrispondente a quella. In effetti, questo è probabilmente l'approccio migliore; tuttavia, dovresti giocare un po 'con la mia versione per capire meglio i trasformatori monad. –

3

Se si desidera eseguire il codice nella interno do puramente nel Maybe Monade, non si avrà accesso alle StateT Int o IO monadi (che potrebbe essere una buona cosa). In questo modo si restituisce un valore Maybe, che si dovrà scrutare:

import Control.Monad 
import Control.Monad.Trans 
import Control.Monad.Trans.State 

type T = StateT Int IO Int 

someMaybe = Just 3 

f :: T 
f = do 
    x <- get 
    -- no need to use bind 
    let mval = do 
     -- this code is purely in the Maybe monad 
     val <- someMaybe 
     -- more code in Maybe monad 
     return 4 
    -- scrutinize the resulting Maybe value now we are back in the StateT monad 
    case mval of 
     Just val -> liftIO . putStrLn $ "I got " ++ show val 
     Nothing -> liftIO . putStrLn $ "I got a rock" 
    return 3