Экземпляр MonadFix для Rand monad
Вопрос
Я хотел бы сгенерировать бесконечный поток чисел с помощью Rand monad из Системный.Случайный.MWC.Монада.Если бы только был экземпляр MonadFix для этой монады, или экземпляр, подобный этому:
instance (PrimMonad m) => MonadFix m where
...
тогда можно было бы написать:
runWithSystemRandom (mfix (\ xs -> uniform >>= \x -> return (x:xs)))
Но такового здесь нет.
Я проходил через это Документы MonadFix но я не вижу очевидного способа реализации этого экземпляра.
Решение
Вопрос: Как вы хотите создать свое начальное семя?
Проблема в том, что 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
.Если вам не нужно быстро генерировать много случайных чисел, вы можете использовать чистую функцию RNG, такую как randomRs
вместо этого.
(Это было бы лучше подходить как комментарий к ответу на Хипсинк, но это слишком долго.)
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
а затем просто повторяя одно значение на неопределенный срок.