Экземпляр MonadFix для монады Rand - PullRequest
4 голосов
/ 20 марта 2011

Я хотел бы создать бесконечный поток чисел с монадой Рэнда из System.Random.MWC.Monad . Если бы только был экземпляр MonadFix для этой монады, или экземпляр, подобный этому:

instance (PrimMonad m) => MonadFix m where
     ...

тогда можно было бы написать:

runWithSystemRandom (mfix (\ xs -> uniform >>= \x -> return (x:xs)))

Хотя нет ни одного.

Я просматривал Документы MonadFix , но я не вижу очевидного способа реализации этого экземпляра.

Ответы [ 3 ]

6 голосов
/ 20 марта 2011

Вы можете написать экземпляр 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.

3 голосов
/ 21 марта 2011

Вопрос: как вы хотите сгенерировать начальное начальное число?

Проблема в том, что MWS построен на "примитивном" пакете, который абстрагирует только IO и строгий (Control.Monad.ST.ST s).Он также не абстрагируется от lazy (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)
2 голосов
/ 14 сентября 2014

(Это лучше подходит как комментарий к ответу 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 и последующее повторение одного значения бесконечно.

...