Instancia de Monadfix para Rand Monad
Pregunta
Me gustaría generar un flujo infinito de números con Rand Monad de System.random.mwc.monad. Si tan solo hubiera una instancia de Monadfix para esta mónada, o una instancia como esta:
instance (PrimMonad m) => MonadFix m where
...
Entonces uno podría escribir:
runWithSystemRandom (mfix (\ xs -> uniform >>= \x -> return (x:xs)))
Aunque no hay uno.
Estaba pasando por Monadfix documentos Pero no veo una forma obvia de implementar esta instancia.
Solución
Una pregunta: ¿Cómo desea generar su semilla inicial?
El problema es que MWS se basa en el paquete "primitivo" que abstrae solo io y estricto (control.monad.st.st s). Tampoco abstrae perezoso (control.monad.st.lazy.st s).
Quizás uno podría hacer instancias para que "primitivo" cubra el st de lazy y luego los MW podrían ser perezosos.
ACTUALIZACIÓN: Puedo hacer que esto funcione usando control.monad.st.lazy usando stricttolazyst:
module Main where
import Control.Monad(replicateM)
import qualified Control.Monad.ST as S
import qualified Control.Monad.ST.Lazy as L
import qualified System.Random.MWC as A
foo :: Int -> L.ST s [Int]
foo i = do rest <- foo $! succ i
return (i:rest)
splam :: A.Gen s -> S.ST s Int
splam = A.uniformR (0,100)
getS :: Int -> S.ST s [Int]
getS n = do gen <- A.create
replicateM n (splam gen)
getL :: Int -> L.ST s [Int]
getL n = do gen <- createLazy
replicateM n (L.strictToLazyST (splam gen))
createLazy :: L.ST s (A.Gen s)
createLazy = L.strictToLazyST A.create
makeLots :: A.Gen s -> L.ST s [Int]
makeLots gen = do x <- L.strictToLazyST (A.uniformR (0,100) gen)
rest <- makeLots gen
return (x:rest)
main = do
print (S.runST (getS 8))
print (L.runST (getL 8))
let inf = L.runST (foo 0) :: [Int]
print (take 10 inf)
let inf3 = L.runST (createLazy >>= makeLots) :: [Int]
print (take 10 inf3)
Otros consejos
Puedes escribir una instancia de Monadfix. Sin embargo, el código no generará un flujo infinito de números aleatorios distintos. El argumento a MFIX es una función que llama uniform
Exactamente una vez. Cuando se ejecute el código, llamará uniform
Exactamente una vez, y cree una lista infinita que contenga el resultado.
Puede probar el código IO equivalente para ver qué sucede:
import System.Random
import Control.Monad.Fix
main = print . take 10 =<< mfix (\xs -> randomIO >>= (\x -> return (x : xs :: [Int])))
Parece que desea utilizar un generador de números aleatorios con estado, y desea ejecutar el generador y recopilar sus resultados perezosamente. Eso no es posible sin un uso cuidadoso de unsafePerformIO
. A menos que necesite producir muchos números aleatorios rápidamente, puede usar una función RNG pura como randomRs
en cambio.
(Esto sería más adecuado como un comentario a la respuesta de Heatsink, pero es demasiado largo).
MonadFix
las instancias deben adherirse a Varias leyes. Uno de ellos es Izquierda que se encoge/thightening:
mfix (\x -> a >>= \y -> f x y) = a >>= \y -> mfix (\x -> f x y)
Esta ley permite reescribir su expresión como
mfix (\xs -> uniform >>= \x -> return (x:xs))
= uniform >>= \x -> mfix (\xs -> return (x:xs))
= uniform >>= \x -> mfix (return . (x :))
Usando otra ley, pureza mfix (return . h) = return (fix h)
, podemos simplificar aún más a
= uniform >>= \x -> return (fix (x :))
y usar las leyes de mónadas estándar y la reescritura fix (x :)
como repeat x
= liftM (\x -> fix (x :)) uniform
= liftM repeat uniform
Por lo tanto, el resultado es de hecho una invocación de uniform
Y luego simplemente repitiendo el valor único indefinidamente.