Экземпляр MonadFix для монады Rand
Я хотел бы генерировать бесконечный поток чисел с монадой Рэнд из System.Random.MWC.Monad. Если бы только был экземпляр MonadFix для этой монады, или экземпляр, подобный этому:
instance (PrimMonad m) => MonadFix m where
...
тогда можно было бы написать:
runWithSystemRandom (mfix (\ xs -> uniform >>= \x -> return (x:xs)))
Там нет ни одного, хотя.
Я просматривал документы MonadFix, но не вижу очевидного способа реализации этого экземпляра.
3 ответа
Вопрос: как вы хотите создать свое начальное семя?
Проблема в том, что MWS построен на "примитивном" пакете, который абстрагирует только IO и строгий (Control.Monad.ST.ST s). Он также не является абстрактным ленивым (Control.Monad.ST.Lazy.ST s).
Возможно, можно было бы сделать примеры, чтобы "примитив" покрывал ленивый ST, а затем MWS мог бы быть ленивым.
ОБНОВЛЕНИЕ: я могу сделать это с помощью Control.Monad.ST.Lazy с использованием 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)
Вы можете написать экземпляр MonadFix. Однако код не будет генерировать бесконечный поток различных случайных чисел. Аргументом для mfix является функция, которая вызывает uniform
ровно один раз. Когда код будет запущен, он будет вызывать uniform
ровно один раз, и создайте бесконечный список, содержащий результат.
Вы можете попробовать эквивалентный код ввода-вывода, чтобы увидеть, что происходит:
import System.Random
import Control.Monad.Fix
main = print . take 10 =<< mfix (\xs -> randomIO >>= (\x -> return (x : xs :: [Int])))
Кажется, что вы хотите использовать генератор случайных чисел с сохранением состояния, и вы хотите запустить генератор и собирать его результаты лениво. Это невозможно без осторожного использования unsafePerformIO
, Если вам не нужно быстро создавать много случайных чисел, вы можете использовать чистую функцию ГСЧ, такую как randomRs
вместо.
(Это было бы лучше подходит как комментарий к ответу Heatsink, но это слишком долго.)
MonadFix
экземпляры должны придерживаться нескольких законов. Один из них остался сжиматься / уменьшаться:
mfix (\x -> a >>= \y -> f x y) = a >>= \y -> mfix (\x -> f x y)
Этот закон позволяет переписать ваше выражение как
mfix (\xs -> uniform >>= \x -> return (x:xs))
= uniform >>= \x -> mfix (\xs -> return (x:xs))
= uniform >>= \x -> mfix (return . (x :))
Используя другой закон, чистота mfix (return . h) = return (fix h)
мы можем еще больше упростить
= uniform >>= \x -> return (fix (x :))
и используя стандартные законы монады и переписывая fix (x :)
как repeat x
= liftM (\x -> fix (x :)) uniform
= liftM repeat uniform
Следовательно, результатом действительно является один вызов uniform
а затем просто повторять одно значение бесконечно.