Ответ 1
Random
- это считывающее устройство, которое считывает генератор
import Control.Monad.Primitive
import System.Random.MWC
newtype Random a = Random {
runRandom :: forall m. PrimMonad m => Gen (PrimState m) -> m a
}
Мы можем тривиально преобразовать a Random a
в ReaderT (Gen (PrimState m)) m a
. Эта тривиальная операция - это то, что вы хотите hoist
, чтобы превратить a Producer ... Random a
в Producer ... IO a
.
import Control.Monad.Trans.Reader
toReader :: PrimMonad m => Random a -> ReaderT (Gen (PrimState m)) m a
toReader = ReaderT . runRandom
Так как toReader
тривиально, то из hoist
не будет никаких накладных расходов из hoist
. Эта функция написана просто для того, чтобы продемонстрировать свою подпись типа.
import Pipes
hoistToReader :: PrimMonad m => Proxy a a' b b' Random r ->
Proxy a a' b b' (ReaderT (Gen (PrimState m)) m) r
hoistToReader = hoist toReader
Здесь есть два подхода. Простой подход заключается в hoist
ваш Consumer
в ту же монаду, скомпоновать трубы и запускать их.
type ReadGenIO = ReaderT GenIO IO
toReadGenIO :: MFunctor t => t Random a -> t ReadGenIO a
toReadGenIO = hoist toReader
int :: Random Int
int = Random uniform
ints :: Producer Int Random x
ints = forever $ do
i <- lift int
yield i
sample :: Show a => Int -> Consumer a IO ()
sample 0 = return ()
sample n = do
x <- await
lift $ print x
sample (n-1)
sampleSomeInts :: Effect ReadGenIO ()
sampleSomeInts = hoist toReader ints >-> hoist lift (sample 1000)
runReadGenE :: Effect ReadGenIO a -> IO a
runReadGenE = withSystemRandom . runReaderT . runEffect
example :: IO ()
example = runReadGenE sampleSomeInts
Там еще один набор инструментов в Pipes.Lift
, о котором должны знать пользователи труб. Это инструменты для работы трансформаторов, таких как монада Random
, распределяя его по Proxy
. Здесь есть готовые инструменты для запуска знакомых трансформаторов из библиотеки трансформаторов. Все они построены из distribute
. Он превращает Proxy ... (t m) a
в t (Proxy ... m) a
, который вы можете запустить один раз с помощью любых инструментов, которые вы используете для запуска t
.
import Pipes.Lift
runRandomP :: PrimMonad m => Proxy a a' b b' Random r ->
Gen (PrimState m) -> Proxy a a' b b' m r
runRandomP = runReaderT . distribute . hoist toReader
Вы можете закончить комбинирование труб и использовать runEffect
, чтобы избавиться от Proxy
s, но вы сами жуете аргумент генератора, объединяя Proxy ... IO r
вместе.