So I'm writing a roguelike and I need to generate some dungeons randomly. I could use system.random but I really want to make it pure. I want to use newPureMT to generate a mersenne twister random source, then pass it into some monad transformers like stateT and readerT and a few others. So that I would end up with something along the lines of:
genDungeon = do
x <- getRandomNumbers
genrooms x
y <- getRandomNumbers
gendoors x
etc
I can't figure out how to do this without staying in the IO monad. For example, it gives this example:
sample (uniform 1 100) :: State PureMT Int
Which means I should be able to:
blah = do
x <- newPureMT
runState genDungeon x
But even typing it into ghci yields the error
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
to which I have absolutely no idea what that means or how to fix it.
I've spent a few days trying to figure this out, and I'm just completly clueless. It is pretty obvious that there are some pretty heavy type signatures and lifting of some stuff that I have to apply to get stuff to work, but I cannot figure out what they are or how to do it.
Edit: Okay, so I was looking through the Mersenne Twister code and I saw that PureMT is an instance of randomgen which means that I can pass it into system.random and get pure code out of it, rendering random-fu unecessary. I still kind of wish I could figure out how to make this code work as random-fu gives you a ton of extra abilities like different random distributions, but his will do for my project.
Edit: Okay, so I ended up with this code:
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
This pretty much allows me to do what I wanted syntactically, and it uses mersenne twister so it should be a heck of a lot faster. The only thing that bothers me is that on every single number generation, it will update the seed in the state monad and I have heard that it is really slow, although I don't know why. But this should be enough for my purposes.