Ecco un semplice codice "di avviamento" che estende il modulo con alcune riformulazioni in termini di stato. Avrai bisogno di guardare un tutorial come il capitolo LYAH mentre sto giocando con loro, penserei. Tralascio le firme, che diventano sempre più complicate, ma interrogare i tipi in ghci sarà istruttivo. È necessario aggiungere
import Control.Monad.State
import Control.Monad.Writer -- for the position-remembering example
Poi il seguente dovrebbe tutto il lavoro utilizzando la definizione di move
step = do -- step the state once with your `move`,
sstate <- get -- whatever the state is
put (move sstate)
-- this little program could also be written: `modify move` which shows the
-- link between what you wrote and State a little more clearly
steps = do -- repeatedly apply `step` to the state
Sstate _ _ ls <- get -- til there are no moves, then stop
if null ls
then return() -- could be: unless (null ls) $ do step ; steps
else step >> steps
stepsIO = do -- do all steps as with `steps`, but print
[email protected](Sstate a b ls) <- get -- the current state at each state update
liftIO $ print sstate
if null ls then liftIO (putStrLn "Done!")
else step >> stepsIO
stepsPrintPosition = do -- do all steps as with `steps`, printing
Sstate _ b ls <- get -- only position at each state update
liftIO $ do putStr "current position: "
print b
if null ls then liftIO (putStrLn "Done!")
else do step
stepsPrintPosition
stepsAccumulatePositions = do -- move through all states as with `steps`
[email protected](Sstate a b ls) <- get -- but use `tell` to keep adding the current
tell [b] -- position to the underlying list
if null ls then return() -- of positions
else step >> stepsAccumulatePositions
example = Sstate (Dir 0 1) (Pos 0 2) "ffff"
Per usare cose come step
, steps
, stepsIO
ecc, applichiamo runState
; questo ci dà una funzione da uno stato a un nuovo stato
runStateT :: StateT s m a -> s -> m (a, s)
Questo naturalmente è solo la funzione di accesso per la definizione newtype
newtype StateT s m a = StateT {runStateT :: s -> m (a, s)}
La confezione ci permette di scrivere fantasia s -> m (a, s)
cose, utilizzando semplici s -> m (a, s)
bit, ma sotto il cappuccio newtype, è sempre solo una funzione s -> m (a, s)
che stiamo scrivendo nella notazione.
Ovviamente, una volta che scegliamo con runStateT
e abbiamo la nostra funzione s -> m (a, s)
, è necessario fornirgli uno stato iniziale. E 'più facile per vedere come funziona mediante test in ghci
>>> example
Sstate (Dir 0 1) (Pos 0 2) "ffff"
>>> runStateT step example -- we step the state once with move
((),Sstate (Dir 0 1) (Pos 0 3) "fff")
>>> runStateT steps example -- we keep stepping till there are no moves
((),Sstate (Dir 0 1) (Pos 0 6) "")
>>> runStateT stepsIO example -- we print state at each state update
Sstate (Dir 0 1) (Pos 0 2) "ffff"
Sstate (Dir 0 1) (Pos 0 3) "fff"
Sstate (Dir 0 1) (Pos 0 4) "ff"
Sstate (Dir 0 1) (Pos 0 5) "f"
Sstate (Dir 0 1) (Pos 0 6) ""
Done!
((),Sstate (Dir 0 1) (Pos 0 6) "")
>>> runStateT stepsPrintPosition example -- print position only at state updates
current position: Pos 0 2
current position: Pos 0 3
current position: Pos 0 4
current position: Pos 0 5
current position: Pos 0 6
Done!
((),Sstate (Dir 0 1) (Pos 0 6) "")
-- the WriterT examples accumulate a 'monoid' of things you keep
-- adding to with `tell xyz` Here we accumulate a [Position]
-- execXYZ and evalXYZ, where they exist, return less information than runXYZ
>>> runWriterT $ runStateT stepsAccumulatePositions example
(((),Sstate (Dir 0 1) (Pos 0 6) ""),[Pos 0 2,Pos 0 3,Pos 0 4,Pos 0 5,Pos 0 6])
>>> execWriterT $ evalStateT stepsAccumulatePositions example
[Pos 0 2,Pos 0 3,Pos 0 4,Pos 0 5,Pos 0 6]
Nel codice di cui sopra che sto usando le mtl
typeclasses e quindi utilizzando runStateT
e runWriterT
di "interpretare" o specializzarsi classe che coinvolge le firme. Si tratta di tipi di calcestruzzo StateT
e WriterT
definiti in Control.Monad.Trans.{State/Writer}
Uno potrebbe omettere le classi e scrivere solo direttamente con quei tipi concreti, importando tali moduli. L'unica differenza potrebbe essere che devi fare lift $ tell [b]
nel caso in cui combino due effetti, stato e scrittura o qualsiasi altra cosa tu voglia chiamarlo.
C'è molto da dire sull'analisi dello stato con cui si sta lavorando, ma emergerà come si potrebbe rielaborarlo, se si pensa a quanto sopra.
Leggere [questo tutorial] (http://learnyouahaskell.com/ for-a-few-monads-more) su alcune monadi, inclusa la monade 'State'. – Bakuriu
Quello che ho trovato confuso con la monade di stato è che lo stato stesso non è in realtà la monade. cos'è un xmonad è una funzione che modifica lo stato e restituisce un valore: s -> (a, s) – mb14
@ mb14 Un modo per capire è che lo stato è sia un parametro aggiuntivo che un valore di ritorno aggiuntivo; una funzione di tipo 'a -> b' diventa una funzione di tipo' a -> s -> (b, s) '. Currying ti fa pensare a questo come prendendo un valore di tipo 'a' e restituendo una nuova funzione che, se data uno stato, può restituire un valore di tipo' b' e un nuovo stato ('a -> (s -> (b, s)). "La monade, tramite l'operatore' >> = ', è proprio ciò che consente di concatenare tali funzioni insieme. In fin dei conti, si finisce con una funzione di tipo' s -> (t, s) ', che trasforma uno stato iniziale in un valore di tipo 't'. – chepner