2012-09-02 13 views
5
class Monad m => MonadState s m | m -> s where 
    -- | Return the state from the internals of the monad. 
    get :: m s 
    get = state (\s -> (s, s)) 

    -- | Replace the state inside the monad. 
    put :: s -> m() 
    put s = state (\_ -> ((), s)) 

    -- | Embed a simple state action into the monad. 
    state :: (s -> (a, s)) -> m a 
    state f = do 
     s <- get 
     let ~(a, s') = f s 
     put s' 
     return a 

instance MonadState s m => MonadState s (MaybeT m) where... 

Perché un'istanza di MonadState necessita di uno stato e di una monade, perché non creare una classe di stato a parametro singolo?Perché utilizzare MultiParamTypeClasses in MonadState

+4

Non sono sicuro di capire l'alternativa che stai suggerendo. Come scriveresti il ​​tipo di 'stato :: (s -> (a, s)) -> m a' senza entrambi' m' e 's'? – Owen

+0

diciamo che dovremmo iniziare da 'class MonadState s dove ...' e facciamo semplicemente 'get :: s' e' put :: s ->() 'senza inserire s in una monade? Potrebbe raggiungere un'implementazione dello stato più semplice in cui non dobbiamo preoccuparci se si tratta di uno stato Forse o uno stato di Either o IO? –

risposta

6

Lasciatemi provare a rispondere alla domanda di Gert nei commenti, perché è una domanda piuttosto diversa.

La domanda è: perché non possiamo solo scrivere

class State s where 
    get :: s 
    put :: s ->() 

Beh, potremmo scrivere questo. Ma ora la domanda è: cosa possiamo farci? E la parte difficile è che se abbiamo un codice con put x e successivamente get, come possiamo collegare lo get allo put in modo che lo stesso valore venga restituito come quello inserito?

E il problema è che, con i tipi () e s, non c'è modo di collegare l'uno all'altro. Puoi provare a implementarlo in vari modi, ma non funzionerà. Non c'è modo di trasportare i dati dallo put allo get (forse qualcuno può spiegarlo meglio, ma il modo migliore per capire è provare a scriverlo).

una monade non è necessariamente l'unico modo per fare operazioni reticolabile, ma è un modo, perché ha all'operatore >> collegare due affermazioni insieme:

(>>) :: m a -> m b -> m b 

modo da poter scrivere

(put x) >> get 

EDIT: Ecco un esempio di utilizzo the StateT instance defined in the package

foo :: StateT Int IO() 
foo = do 
    put 3 
    x <- get 
    lift $ print x 

main = evalStateT foo 0 
+0

puoi aggiungere un esempio di IO alla tua risposta? Mi piace '(put x) >> get >> = (\ x -> print x)' o qualcosa del genere? –

+1

@GertCuykens Non riesco a pensare a come farlo con 'IO'; 'IO' non ha un posto dove mettere lo stato come' State' fa. Ma posso aggiungere un esempio usando 'StateT' attorno a' IO'. – Owen

+0

@Owen - puoi fare un esempio usando solo 'IO' con' IORef'. –

4

È necessario un modo per associare il tipo di stato al tipo di monade. MultiParamTypeClasses con FunctionalDependencies è a senso unico. Tuttavia, puoi farlo anche usando TypeFamilies.

class (Monad m) => MonadState m where 
    type StateType m 

    -- | Return the state from the internals of the monad. 
    get :: m (StateType m) 
    get = state (\s -> (s, s)) 

    -- | Replace the state inside the monad. 
    put :: StateType m -> m() 
    put s = state (\_ -> ((), s)) 

    -- | Embed a simple state action into the monad. 
    state :: (StateType m -> (a, StateType m)) -> m a 
    state f = do 
     s <- get 
     let ~(a, s') = f s 
     put s' 
     return a 

instance MonadState m => MonadState (MaybeT m) where 
    type StateType (MaybeT m) = StateType m 

    ... 

Questo è l'approccio adottato dallo monads-tf package.

+1

Nota: 'monads-tf' è sostanzialmente ritirato. In origine, Ross divideva 'mtl' in' trasformatori' che era Haskell 98 e due pacchetti 'monads-tf' e' monads-fd' in modo che le persone potessero scegliere quale stile preferivano. Tuttavia, questo è stato terribile in quanto ha diviso i modi della community 3, in quanto 'mtl',' monads-tf' e 'monads-fd' utilizzavano tutti gli stessi nomi dei moduli! La soluzione era di ritirare il vecchio 'mtl', e rendere' monads-fd' in 'mtl' e lasciare' monads-tf' die. Non farei obiezioni a un pacchetto con le classi di 'monads-tf' che si trovava di lato usando nomi di moduli diversi ma, così com'è, è decisamente dannoso. –

Problemi correlati