2009-06-24 57 views
12

Ho una funzione:Come fa 'ottenere' effettivamente/ottenere/lo stato iniziale in Haskell?

test :: String -> State String String 
test x = 
    get >>= \test -> 
    let test' = x ++ test in 
    put test' >> 
    get >>= \test2 -> put (test2 ++ x) >> 
    return "test" 

posso più o meno capire cosa va avanti per tutta la funzione, e sto iniziando a ottenere il blocco di monadi. Quello che non capisco è come, quando si esegue questo:

runState (test "testy") "testtest" 

la funzione di 'entrare' nel 'test' in qualche modo ottiene lo stato "testtest" iniziale. Qualcuno può rompere questo e spiegarmelo?

Apprezzo qualsiasi risposta!

risposta

18

Inizialmente avrei postato questo post come commento, ma ho deciso di esporre un po 'di più.

In senso stretto, get non "accetta" un argomento. Penso che molto di ciò che sta accadendo sia mascherato da ciò che non vedi, le definizioni di istanza della monade di Stato.

get è in realtà un metodo della classe MonadState. La monade Stato è un'istanza di MonadState, fornendo la seguente definizione di get:

get = State $ \s -> (s,s) 

In altre parole, get solo restituisce una monade Stato molto di base (ricordando che una monade può essere pensato come un "wrapper" per un calcolo), in cui qualsiasi input s nel calcolo restituirà una coppia di s come risultato.

La prossima cosa che dobbiamo guardare è >>=, quale Stato definisce nel seguente modo:

m >>= k = State $ \s -> let 
    (a, s') = runState m s 
    in runState (k a) s' 

Quindi, >>= sta per produrre un nuovo calcolo, che non sarà calcolato fino a quando non ottiene uno stato iniziale (questo è vero per tutti i calcoli statali quando sono nella loro forma "incartata"). Il risultato di questo nuovo calcolo si ottiene applicando ciò che si trova sul lato destro dello >>= al risultato dell'esecuzione del calcolo che era sul lato sinistro.(Che è una frase piuttosto confusa che può richiedere un ulteriore lettura o due.)

ho trovato molto utile per "desugar" tutto ciò che sta succedendo. Ciò richiede molto più digitazione, ma dovrebbe rendere molto chiara la risposta alla tua domanda (da cui il get arriva). Si noti che la segue deve essere considerato psuedocodarlo ...

test x = 
    State $ \s -> let 
     (a,s') = runState (State (\s -> (s,s))) s --substituting above defn. of 'get' 
     in runState (rightSide a) s' 
     where 
      rightSide test = 
      let test' = x ++ test in 
      State $ \s2 -> let 
      (a2, s2') = runState (State $ \_ -> ((), test')) s2 -- defn. of 'put' 
      in runState (rightSide2 a2) s2' 
      rightSide2 _ = 
      -- etc... 

Questo dovrebbe rendere evidente che il risultato finale della nostra funzione è un nuovo calcolo Stato che avrà bisogno di un valore iniziale (s) per rendere il resto del succedono cose Hai fornito s come "testtest" con la tua chiamata runState. Se si sostituisce "testtest" per s nel pseudocodice sopra, vedrete che la prima cosa che succede è corriamo get con "testtest", come il 'stato iniziale'. Questo produce ("testtest", "testtest") e così via.

Ecco dove get ottiene il vostro stato iniziale "testtest". Spero che questo ti aiuti!

+2

Non riuscivo a pensare a una parola migliore quando ho detto che "prendeva" una discussione. Grazie per questa spiegazione molto dettagliata. – Rayne

5

Potrebbe essere utile dare un'occhiata più approfondita a ciò che è realmente il costruttore del tipo State ea come viene utilizzato da runState. In GHCi:

Prelude Control.Monad.State> :i State 
newtype State s a = State {runState :: s -> (a, s)} 
Prelude Control.Monad.State> :t runState 
runState :: State s a -> s -> (a, s) 

State prende due argomenti: il tipo di stato, e il tipo restituito. È implementato come una funzione che assume lo stato iniziale e produce un valore di ritorno e il nuovo stato.

runState assume tale funzione, l'input iniziale, e (probabilmente) solo vale una all'altra per recuperare il (risultato, stato) coppia.

La funzione test è una grande composizione di State funzioni -tipo, ciascuna un ingresso stato e ottenendo un (risultato, stato) di uscita, collegato tra loro in modo che abbia senso al programma. Tutto lo runState fa fornire loro un punto di partenza dello stato.

In questo contesto, get è semplicemente una funzione che prende stato come input, e restituisce una (risultato, state) in uscita tale che il risultato è lo stato di input, e lo stato è invariato (lo stato dell'uscita è l'ingresso stato). In altre parole, get s = (s, s)

+1

Clicked clic click! Bingo. Grazie mille, la tua spiegazione è fatta clic. Non ho mai capito che in realtà "prendevo" una discussione. Hehe. Grazie. – Rayne

1

Passando attraverso il capitolo 8 ("Functional Parsers") di Graham Hutton Programming in Haskell diverse volte fino a quando non l'avevo capito correttamente, seguito da un passaggio al tutorial All About Monads, fatto questo clic per me.

Il problema con monadi è che sono molto utili per diverse cose che quelli di noi provenienti dalla solita background di programmazione trovare abbastanza dissimili. Ci vuole un po 'di tempo per capire che il flusso di controllo e lo stato di gestione non sono solo abbastanza simili da poter essere gestiti dallo stesso meccanismo, ma sono quando si fa un passo indietro abbastanza, la stessa cosa.

Un'epifania venne quando stavo considerando le strutture di controllo in C (for e while, ecc.) E mi resi conto che di gran lunga la struttura di controllo più comune stava semplicemente mettendo una dichiarazione prima dell'altra. Ci è voluto un anno per studiare Haskell prima che mi rendessi conto che anche questa era una struttura di controllo.

+0

Grazie per la risposta. Fortunatamente, la maggior parte della mia esperienza di programmazione si trova in realtà nella programmazione funzionale, quindi non è così difficile per me! : D – Rayne

Problemi correlati