Monadfix Instanza per Rand Monad
Domanda
Vorrei generare un flusso infinito di numeri con Rand Monad da System.random.mwc.monad. Se solo ci sarebbe un'istanza Monadfix per questa monade, o istanza come questa:
instance (PrimMonad m) => MonadFix m where
...
Quindi si potrebbe scrivere:
runWithSystemRandom (mfix (\ xs -> uniform >>= \x -> return (x:xs)))
Non ce n'è uno però.
Stavo attraversando Monadfix Docs Ma non vedo un modo ovvio di implementare questa istanza.
Soluzione
Una domanda: come desideri generare il tuo seme iniziale?
Il problema è che MWS è costruito sul pacchetto "primitivo" che estrae solo IO e rigoroso (control.monad.st.st s). Non è anche astratto Lazy (Control.Monad.st.lazy.st S).
Forse si potrebbero fare casi per "primitivi" per coprire Lazy ST e quindi MWS potrebbe essere pigro.
AGGIORNAMENTO: posso fare questo lavoro utilizzando control.monad.st.lazy usando StrictoLazyst:
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)
Altri suggerimenti
Puoi scrivere un'istanza MonadFix. Tuttavia, il codice non genererà un flusso infinito di numeri casuali distinti. L'argomento su MFIX è una funzione che chiama uniform
Esattamente una volta. Quando il codice viene eseguito, chiamerà uniform
Esattamente una volta e crea un elenco infinito contenente il risultato.
Puoi provare il codice IO equivalente per vedere cosa succede:
import System.Random
import Control.Monad.Fix
main = print . take 10 =<< mfix (\xs -> randomIO >>= (\x -> return (x : xs :: [Int])))
Sembra che tu voglia utilizzare un generatore di numeri casuali statali e vuoi eseguire il generatore e raccogliere pigramente i suoi risultati. Non è possibile senza un uso attento di unsafePerformIO
. A meno che tu non abbia bisogno di produrre molti numeri casuali rapidamente, è possibile utilizzare una funzione RNG pura come randomRs
invece.
(Questo sarebbe più adatto come un commento alla risposta di HeatSink, ma è un po 'troppo lungo.)
MonadFix
Le istanze devono aderire Diverse leggi. Uno di loro è a sinistra che si restringe/thighinging:
mfix (\x -> a >>= \y -> f x y) = a >>= \y -> mfix (\x -> f x y)
Questa legge consente di riscrivere la tua espressione come
mfix (\xs -> uniform >>= \x -> return (x:xs))
= uniform >>= \x -> mfix (\xs -> return (x:xs))
= uniform >>= \x -> mfix (return . (x :))
Usando un'altra legge, purezza mfix (return . h) = return (fix h)
, possiamo semplificare ulteriormente
= uniform >>= \x -> return (fix (x :))
e usando le leggi standard della monade e la riscrittura fix (x :)
come repeat x
= liftM (\x -> fix (x :)) uniform
= liftM repeat uniform
Pertanto, il risultato è davvero un'invocazione di uniform
E poi ripetere il valore singolo indefinitamente.