3 ms·
On the one hand, of course you can. But I do understand how it might seem tricky if you're too focused on trying to glue together `Gen a`s (or even `MonadRando
by dllthomas 6y ago
On the one hand, of course you can. But I do understand how it might seem tricky if you're too focused on trying to glue together `Gen a`s (or even `MonadRandom m => m a`s), principally with Monad.
To the grandparent, assuming the trouble was as I understand it, the idea is to split your state and advance one new state down the lazy list while the rest of the computation uses the other new state.
Eliding constraints for brevity, if you have:
split :: g -> (g, g)
random :: g -> (a, g)
then you can say
lazyRandomList :: g -> ([a], g)
lazyRandomList oldState =
let (listState, newState) = split oldState
in (makeList listState, newState)
where
makeList inState =
let (value, nextState) = random inState
in value : makeList nextState
When you match on the outer tuple, the split gets forced, but `makeList listState` is still a thunk. You can go ahead and use `newState` all you want without forcing it.
Once you force it, you have a cons, with two thunks that can be forced independently. The tail evaluates to a similar cons produced from a different state. A lazy list.
It's true that it's infinite, but you can fix that (if you want to) by taking the head (perhaps a random number of elements); it's easier to control the distribution of length that way than with some fixed chance you produce nil at each step, although that approach works too.