2016-03-23 27 views
7

Un esempio di vita reale: se sono di buon umore ("buono stato"), quando il manager mi chiede delle stime, gli do una risposta solida, ma oso farlo 3 volte in una riga, senza alcuni snack gratuiti in mezzo, il mio umore cambia (arrivo al "cattivo stato") e le prossime 3 volte che mi avvicino gli chiedo di non disturbarmi con nessuna delle sue sciocchezze.Perché la firma runState ha solo argomenti di stato?

Ecco un registro di mio solito giorno:

       [ Mood: Good, Patience: 3 ] -- 11:00 am, I'm happy 
ESTIMATE -> "bla bla 6",  [ Mood: Good, Patience: 2 ] 
ESTIMATE -> "bla bla 1",  [ Mood: Good, Patience: 1 ] 
Cookies! -> "",    [ Mood: Good, Patience: 3 again! ] 
ESTIMATE -> "bla bla 7",  [ Mood: Good, Patience: 2 ] 
ESTIMATE -> "bla bla 2",  [ Mood: Good, Patience: 1 ] 
ESTIMATE -> "bla bla 9",  [ Mood: BAD , Patience: -2 ] -- Enough! 
ESTIMATE -> "Need a break!" [ Mood: BAD , Patience: -1 ] 
ESTIMATE -> "Deploynig!", [ Mood: BAD , Patience: 0 ] 
ESTIMATE -> "Lunch time!", [ Mood: Good, Patience: 3 ] -- Ok he needs me.. 
ESTIMATE -> "bla bla 6",  [ Mood: Good, Patience: 2 ] 
... 

Ora questo modello mi sembra al lavoro per adattarsi al State Monade.

newtype State s a = State { runState :: s -> (a, s) } 

Ma come si fa? La firma ha spazio per uno stato, che nel mio caso è (Mood,Patience) e non per l'input (ESTIMATE o Cookies). È come se dovessi rispondere senza nemmeno ascoltare!

Quindi la mia domanda è: Come faccio a rendere non solo statico ma anche il calcolo Argumentful con State monade di Haskell?

risposta

8

Un computo stateful riceve un input, uno stato e restituisce un output e un nuovo stato. Quindi il tipo sarà input -> state -> (state, output).

Il runState è solo un calcolo stateful parzialmente applicato che ha già preso i suoi input.

noti inoltre che quando la composizione di funzioni stato (cioè quando si utilizza l'operatore >>= bind o do notazione) fare esattamente questo: si fornisce gli ingressi come espressione e il binding è responsabile di passare intorno solo il stato.

È possibile chiamare get senza utilizzare il suo valore di ritorno, ma poi si perde. Se si desidera utilizzarlo, è necessario utilizzare value <- get e quindi fornire lo value come input esplicito per il successivo calcolo stateful. Il legame entra in gioco solo quando passa lo stato intorno.


Esempio pratico: si consideri la funzione:

doStuff :: Int -> State Int Int 
doStuff x = do 
    val <- get 
    put $ val+x+1 
    return 0 

Il tipo doStuff ha esattamente lo schema input -> state -> (state, output). Ma la parte input è rappresentata dall'argomento x. Una volta fornito x si ottiene qualcosa di tipo state -> (state, output) che rappresenta esattamente ciò che rappresenta runState.

Quindi non hai realmente bisogno degli argomenti per le azioni stateful perché puoi applicarli parzialmente in anticipo per ottenere "calcoli con stato puro che non hanno input" (sono citazioni spaventose).

2

suona come quello che stai cercando non è State ma StateT, un trasformatore monade che aggiunge statefulness ad una monade esistente.

newtype StateT s m a = StateT (s -> m (a, s)) 

Dato uno stato di tipo s, un'azione StateT s m a restituisce un m un'azione che, quando eseguito, produce un risultato e un nuovo stato. StateT s è un'istanza di MonadTrans:

instance MonadTrans (StateT s) where 
    --lift :: Monad m => m a -> StateT s m a 
    lift ma = StateT $ 
    \s -> ma >>= \a -> pure (a, s) 

Inoltre, se m è un Monad, allora lo è StateT s m.

Quindi, se si vuole prendere "input" entro un contesto (ad esempio, IO), siete liberi di farlo:

do 
    s <- get 
    input <- lift getLine 
    when (annoying input && annoyed s) $ put Angry 

Per IO in particolare, di solito è meglio usare liftIO, che può sollevare tutta una serie di trasformatori.

Problemi correlati