Ciao gente,Creazione di monadi analoga aperta al IO Monade con lo stato concatenato
Sono abbastanza nuovo a Haskell anche quest'anno (dopo averlo utilizzato nei primi anni 1990 e poi di nuovo nei primi anni 00 del). Sto cercando di scrivere del codice che utilizza un modello che è analoga aperta quasi direttamente al example IO monad shown on the Haskell Wiki:
type IO a = RealWorld -> (a, RealWorld)
(Sì, so che questo non è l'attuazione GHC di IO, ma semplicemente un veicolo per la comprensione .) La ragione è che nella mia applicazione (un gioco), ora ho due pattern che fanno questo con due diverse sostituzioni dello RealWorld
qui. In uno, è lo stato del gioco, e nell'altro, è solo un seme numero casuale StdGen
. Io, naturalmente, hanno ora due coppie di tipi come questo:
-- | Easily return a specified value as well as the new random number generator
type ReturnRNG a = (a, StdGen)
-- | Take an RNG and return it and another value.
-- (This is basically like the IO type but with the StdGen instead of RealWorld.)
type WithRNG a = StdGen -> ReturnRNG a
-- | Easily return a specified value as well as the new GameState
type ReturnGS a = (a, GameState)
-- | Use a GameState and return a value with the updated GameState.
-- (This is like IO.)
type WithGS a = GameState -> ReturnGS a
(Sì, ho potuto li astratto in una coppia con due parametri, ma non ho ottenuto intorno ad esso.) Si può vedere, naturalmente, che i miei tipi e WithRNG a
(sinonimi di tipo) sono esattamente analoghi a IO a
precedente.
Quindi, ecco un semplice esempio di codice di lavoro effettivo che ora ho:
-- | Returns a random position for the given size.
randomPos :: (Int, Int) --^The size
-> WithRNG (Int, Int) --^The result (0 up to 1 less than the size) and new RNG seed
randomPos (w, h) r0 = ((x, y), r2)
where
(x, r1) = randomR (0, w - 1) r0
(y, r2) = randomR (0, h - 1) r1
Questo crea una coppia casuale in un intervallo specificato, e restituisce il seme RNG finale. Una grande percentuale dei miei metodi è così (usando WithRNG
o WithGS
), utilizzando uno stato concatenato, a volte anche fino a r4
o r6
(o gs4
, ecc.). Preferisco scrivere questo esempio per assomigliare a questo ...
-- (Not working example)
randomPosSt (w, h) = do
x <- randomR (0, w - 1)
y <- randomR (0, h - 1)
return (x, y)
... eppure hanno lo stesso identico metodo di firma e semantica. Questo sembra che dovrebbe essere possibile seguendo il tutorial di cui sopra che dà questo esempio:
(>>=) :: IO a -> (a -> IO b) -> IO b
(action1 >>= action2) world0 =
let (a, world1) = action1 world0
(b, world2) = action2 a world1
in (b, world2)
Questo, come potete vedere, è quasi esattamente quello che sto facendo in precedenza (una volta che si sostituisce "let
" per "where
" notazione).
Tuttavia, non posso creare un Monade di un sinonimo di tipo. (Ho provato TypeSynonymInstances ma non sembra funzionare con "instance Monad WithRNG where
" o usando un parametro.Utilizzando un newtype
sembrerebbe anche aggiungere inutile sintassi brutta.) Non sono stato in grado di capire bene la Monad di stato abbastanza per fare un metodo equivalente usando quello. Anche se avessi avuto successo, tuttavia, l'implementazione della Monad di stato sembrerebbe usare brutti "get
" e "put
" s (e "runState
" ecc.) E rendere il codice inferiore a leggibile, non di più.
-- THIS DOES NOT WORK
-- | Make a State Monad with random number generator - like WithRNG above
type RandomState = State StdGen
-- | Returns a random position for the given size.
randomPosSt :: (Int, Int) --^The size
-> RandomState (Int, Int) --^The result (0 up to 1 less than the size) and new RNG seed
Dopo tutto questo, ho concluso che io sono sia facendo qualcosa di sbagliato, incomprensione qualcosa, o semplicemente non posso fare quello che voglio fare. Stavo per dire "beh, non hai davvero bisogno di capire come modificare il tuo codice per far sì che lo stato venga gestito automaticamente, perché funziona bene" e rinunciare e ho pensato che avrei chiedi qui (il mio debutto delirante). Preferirei una soluzione più elegante.
Ho anche figura una soluzione più elegante mi avrebbe dato questa funzione che uso "per libero:"
-- | Maps the specified method, which must take a RNG as the last parameter,
-- over all the elements of the list, propagating the RNG and returning it.
-- TODO: Implement this without using recursion? Using a fold?
mapRandom :: (a -> WithRNG b) --^The function to map (that takes a RNG)
-> [a] --^The input list
-> WithRNG [b] --^The RNG to return
mapRandom func [] r0 = ([], r0)
mapRandom func (x:xs) r0 = (mapped : rest, r2)
where
(mapped, r1) = func x r0
(rest, r2) = mapRandom func xs r1
Grazie per ogni pensiero, suggestioni, riferimenti e il vostro tempo!
Prima di rispondere, sei a conoscenza della monade 'State'? –
@GabrielGonzalez In effetti ho sentito parlare della monade di Stato: "Non sono stato in grado di capire abbastanza bene la Monade di Stato per fare un metodo equivalente usando quello. Anche se avessi avuto successo, tuttavia, l'implementazione della Monade di Stato sembrerebbe usare brutti "get" e "put" s (e "runStates" ecc.) e rendere il codice meno leggibile, non di più. " –
Prima di rispondere, sei a conoscenza del trasformatore monad ['RandT'] (http://hackage.haskell.org/packages/archive/MonadRandom/0.1.8/doc/html/Control-Monad-Random.html) ? –