I have a piece of code that I’d like to make faster (it’s a bit of library code that is used heavily, so investing time in optimization seems worthwhile). I suspect the answer involves fairly routine intuitions about performance in Haskell, but I’m a bit of a novice in that area.
A have a program like:
ex = do
x <- random
y <- random
return (x,y)
The idea is that I’d like to “run” ex by providing a list of doubles, one for each call to random in the program. Running the program should run from the top down, and each time a random is encountered, a number should be removed from the head of the list, used as the value for random, and recorded. If the list of doubles is empty, then a value for the call to random should be drawn from an underlying source of randomness, but still recorded in the same way.
Here’s an implementation using MonadWriter and MonadState:
random = do
trace <- get
x <- case trace of
[] -> underlyingSourceOfRandomness
r : xs -> put xs >> pure r
tell [x]
pure x
Now my guess is that the standard WriterT monad transformer is a bad idea here, so I figured I’d use the one from Control.Monad.Trans.Writer.CPS. As for StateT, perhaps the strict variant?
I’d expect the performance of an optimally written version of this code to be linear in the number of calls to random, which I haven’t yet been able to attain.
The version in the codebase I’m working with is here at line 75. It used the Church-transformed free monad transformer to represent a program and then interprets it in using State and Writer as described above.
It’s fast, but I don’t know if it’s optimal (at least, not linear), and more importantly, I don’t really understand exactly what is making the other versions slow, other than having a vague sense that Writer space leaks.