Domanda

Ho una funzione:

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

Riesco praticamente a capire cosa succede durante questa funzione e sto cominciando a capire come vanno le monadi. Quello che non capisco è come, quando eseguo questo:

runState (test "testy") "testtest"

la funzione 'get' in 'test' in qualche modo ottiene lo stato iniziale " testtest " ;. Qualcuno può scomporlo e spiegarmelo?

Apprezzo qualsiasi risposta!

È stato utile?

Soluzione

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

A rigor di termini, get non " accetta " un argomento. Penso che molto di ciò che sta succedendo sia mascherato da ciò che non vedi: le definizioni di istanza della monade dello Stato.

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

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

In altre parole, get restituisce semplicemente una monade di stato molto semplice (ricordando che una monade può essere pensata come un "involucro" per un calcolo), dove qualsiasi input s nel calcolo restituirà una coppia di s come risultato.

La prossima cosa che dobbiamo guardare è > > = , che lo Stato definisce così:

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

Quindi, > > = produrrà un nuovo calcolo, che non verrà calcolato fino a quando non avrà uno stato iniziale (questo vale per tutti i calcoli di stato quando sono in il loro modulo "incartato"). Il risultato di questo nuovo calcolo si ottiene applicando tutto ciò che si trova sul lato destro del > > = al risultato dell'esecuzione del calcolo che era sul lato sinistro. (È una frase piuttosto confusa che potrebbe richiedere una lettura aggiuntiva o due.)

Ho trovato abbastanza utile " desugar " tutto quello che sta succedendo. Ciò richiede molta più digitazione, ma dovrebbe rendere molto chiara la risposta alla tua domanda (da dove get ). Si noti che quanto segue deve essere considerato psuedocode ...

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...

Ciò dovrebbe rendere ovvio che il risultato finale della nostra funzione è un nuovo calcolo dello Stato che avrà bisogno di un valore iniziale ( s ) per far accadere il resto delle cose. Hai fornito s come " testtest & con la tua chiamata runState . Se sostituisci " testtest " per s nello pseudocodice sopra, vedrai che la prima cosa che succede è che eseguiamo get con " testtest " come "stato iniziale". Questo produce (" testtest " ;, " testtest ") e così via.

Ecco dove get ottiene il tuo stato iniziale " testtest " ;. Spero che questo aiuti!

Altri suggerimenti

Potrebbe aiutarti a dare uno sguardo più approfondito al vero costruttore di tipo State e al modo in cui runState lo utilizza. 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 accetta due argomenti: il tipo di stato e il tipo restituito. È implementato come una funzione che prende lo stato iniziale e produce un valore di ritorno e il nuovo stato.

runState accetta tale funzione, l'input iniziale e (molto probabilmente) si applica l'uno all'altro per recuperare la coppia (risultato, stato).

La tua funzione test è una grande composizione delle funzioni di tipo State , ciascuna delle quali prende un input di stato e produce un output (risultato, stato), collegati l'uno all'altro in un modo che ha senso per il tuo programma. Tutto ciò che runState fornisce loro un punto di partenza dello stato.

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

Esaminando il capitolo 8 (" Functional Parser ") di di Graham Hutton Programmare in Haskell diverse volte fino a quando non l'avessi capito bene, seguito da un tutorial All About Monads , ha fatto questo clic per me.

Il problema con le monadi è che sono molto utili per diverse cose che quelli di noi che provengono dal solito background di programmazione trovano abbastanza diversi. Ci vuole del 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 lo sono quando si fa abbastanza indietro, la stessa cosa.

Un'epifania venne quando stavo considerando le strutture di controllo in C ( per e mentre , ecc.), e mi resi conto che la struttura di controllo di gran lunga più comune era semplicemente mettendo una dichiarazione prima dell'altra. Ci è voluto un anno di studio su Haskell prima di rendermi conto che quella era persino una struttura di controllo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top