Экземпляр 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 а затем просто повторять одно значение бесконечно.

Другие вопросы по тегам