Finalmente me di cuenta de cómo usar las mónadas (no sé si las entiendo ...), pero mi código nunca es muy elegante. Supongo que es por la falta de control sobre cómo todas esas funciones en Control.Monad
realmente pueden ayudar. Así que pensé que sería bueno pedir consejos sobre esto en un código en particular usando la mónada estatal.¿Consejos para un código más elegante con mónadas?
El objetivo del código es calcular muchos tipos de paseos aleatorios, y es algo que intento hacer antes de algo más complicado. El problema es que tengo dos cómputos con estado, al mismo tiempo, y me gustaría saber cómo componer con elegancia:
- la función que actualiza el generador de números aleatorios es algo del tipo
Seed -> (DeltaPosition, Seed)
- La función que actualiza la posición del caminador aleatorio es algo del tipo
DeltaPosition -> Position -> (Log, Position)
(dondeLog
es solo una forma de informar cuál es la posición actual del caminador aleatorio).
Lo que he hecho es lo siguiente:
Tengo una función para componer estos dos cómputos con estado:
composing :: (g -> (b, g)) -> (b -> s -> (v,s)) -> (s,g) -> (v, (s, g))
composing generate update (st1, gen1) = let (rnd, gen2) = generate gen1
(val, st2) = update rnd st1
in (val, (st2, gen2))
y entonces convertirlo en una función que componen los estados:
stateComposed :: State g b -> (b -> State s v) -> State (s,g) v
stateComposed rndmizer updater = let generate = runState rndmizer
update x = runState $ updater x
in State $ composing generate update
Y luego tengo la cosa más simple, por ejemplo, un andador aleatorio que sumará un número aleatorio a su posición actual:
update :: Double -> State Double Double
update x = State (\y -> let z = x+y
in (z,z))
generate :: State StdGen Double
generate = State random
rolling1 = stateComposed generate update
y una función para hacer esto en varias ocasiones:
rollingN 1 = liftM (:[]) rolling1
rollingN n = liftM2 (:) rolling1 rollings
where rollings = rollingN (n-1)
Y luego, si me carga esta en ghci
y corro:
*Main> evalState (rollingN 5) (0,mkStdGen 0)
[0.9872770354820595,0.9882724161698186,1.9620425108498993,2.0923229488759123,2.296045158010918]
consigo lo que quiero, que es una lista de las posiciones ocupadas por el caminante al azar. Pero ... Siento que debe haber una manera más elegante de hacer esto. Tengo dos preguntas:
¿Puedo reescribir las funciones de una manera más "monádico", utilizando las funciones inteligentes de
Control.Monad
?¿Existe un patrón general sobre cómo combinar estados como este que se pueden usar? ¿Tiene esto algo que ver con los transformadores de mónada o algo así?
Por cierto, es una buena idea evitar el uso del 'State' constructor de datos, ya que en el sucesor de' mtl' ('mónadas-fd'),' State' se define en términos de 'StateT' y, por lo tanto, el constructor de datos' State' no existe. –
@TravisBrown En realidad, 'monads-fd' está en desuso en favor de' mtl'. (Reconociendo que su comentario tiene 5 años) – crockeea