Итак, я пишу roguelike и мне нужно генерировать несколько темниц случайным образом. Я мог бы использовать system.random, но я действительно хочу сделать его чистым. Я хочу использовать newPureMT, чтобы сгенерировать случайный источник мерсеннового твистера, а затем передать его в некоторые монадные преобразователи, такие как stateT и readerT и некоторые другие. Так что я бы закончил с чем-то вроде:
genDungeon = do
x <- getRandomNumbers
genrooms x
y <- getRandomNumbers
gendoors x
etc
Я не могу понять, как это сделать, не оставаясь в монаде IO. Например, он дает этот пример:
sample (uniform 1 100) :: State PureMT Int
Что означает, что я должен быть в состоянии:
blah = do
x <- newPureMT
runState genDungeon x
Но даже если ввести его в ghci, вы получите ошибку
Overlapping instances for Data.Random.Lift.Lift
Data.Functor.Identity.Identity
(StateT PureMT Data.Functor.Identity.Identity)
arising from a use of `sample' at <interactive>:1:0-28
Matching instances:
instance [incoherent] (Monad m, MonadTrans t) =>
Data.Random.Lift.Lift m (t m)
-- Defined in Data.Random.Lift
instance [incoherent] (Monad m) =>
Data.Random.Lift.Lift Data.Functor.Identity.Identity m
-- Defined in Data.Random.Lift
На что я абсолютно не знаю, что это значит или как это исправить.
Я потратил несколько дней, пытаясь понять это, и я просто совершенно не в курсе. Совершенно очевидно, что есть некоторые довольно тяжелые подписи типов и отмена некоторых вещей, которые я должен применить, чтобы заставить вещи работать, но я не могу понять, кто они и как это сделать.
Редактировать: Хорошо, поэтому я просматривал код Mersenne Twister и увидел, что PureMT является экземпляром randomgen, что означает, что я могу передать его в system.random и получить из него чистый код, что делает random-fu ненужным , Я все еще хотел бы выяснить, как заставить этот код работать так, как random-fu дает вам массу дополнительных возможностей, таких как различные случайные распределения, но он подойдет для моего проекта.
Редактировать: Хорошо, поэтому я получил этот код:
rand :: (RandomGen g, MonadState g m) => Int -> Int -> m Int
rand lo hi = do
r <- get
let (val, r') = randomR (lo, hi) r
put r'
return val
gendungeons = replicateM 10 $ do
x <- rand 0 24
y <- rand 4 10
z <- replicateM 10 $ rand 5 50
let dungeon = makeadungeonpurelywiththeserandomvalues x y z
return dungeon
test = do
x <- newPureMT
let dungeons = runState gendungeons x
return dungeons
Это в значительной степени позволяет мне делать то, что я хотел синтаксически, и использует мерсенновый твистер, так что это должно быть чертовски быстрее. Единственное, что меня беспокоит, это то, что при каждом поколении чисел оно обновляет начальное число в монаде состояния, и я слышал, что оно действительно медленное, хотя я не знаю почему. Но этого должно быть достаточно для моих целей.