I am trying to recursively make a list of random numbers that uses the previous value to get the next (so recursion is required instead of map or fold, and also I prefer to make it explicit unless map/foldr makes it ridiculously simple in comparison).
Using a pure PRNG this is very straightforward and idiomatic, in my opinion (puregaussian uses System.Random to generate a normal variate and has type puregaussian :: System.Random.RandomGen t => t -> Double -> Double -> (Double, t)).
purecurse :: System.Random.RandomGen t => t -> Double -> [Double] -> [Double]
purecurse gen current [] = []
purecurse gen current (x:xs) = let (rand, gen2) = puregaussian gen 0 1
next = current + rand
in current:purecurse gen2 next xs
Unfortunately, pure PRNGs don't seem do be as well developed in Haskell as the monadic ones, so I want to do the same thing using a library like random-fu or mwc-probability, and the solutions I found to work are either unidiomatic, not as concise, or both.
Here's a solution using do notation that works, and why I'm not satisfied with it:
import Control.Monad.Primitive
import System.Random.MWC.Probability
recurse :: PrimMonad m => Gen (PrimState m) -> [Double] -> [Double] -> m [Double]
recurse gen history@(current:_) [] = return history
recurse gen history@(current:_) (x:xs) = do
rand <- (sample (normal 0 1) gen)
let next = current + rand
recurse gen (next:history) xs
First of all I would rather use >>= than do notation, but I couldn't find a way of binding the rand variable that has type m Double and then lifting it to get m [Double] at the end case. There doesn't seem to be a lot of documentation (that I could find) or examples on how to do something like that. I thought maybe it would be necessary to nest the (>>=) operators, but that could make the function extremely complicated or unreadable. If that is the tradeoff, maybe do notation is just cleaner, but I didn't manage to make even that work and would like to know how to.
Second, the function requires the entire list to be passed on at each call, and gives the list back in reverse (and just switching next and history breaks it).
So. I would like to be able to pass the initial state and a list to recurse over that returns a monadic list of values.
The main question I would like help with is: is there a Haskell idiomatic way of writing such a recursion of monadic values resulting in a monadic list that is similar to the structure of a pure function?
Aucun commentaire:
Enregistrer un commentaire