I am trying to run an "infinite" simulation, printing the results of each step.
There's a function nextFrameR
which takes an input Map
and steps forward the simulation to return an output Map
, and then there's a render_
function which takes an input Map
and prints some things to stdout
, returning the input Map
(so that I can use iterate
or something like it).
I'm really struggling to put all these pieces together as am relatively new to Haskell. I found this answer extremely interesting but am not sure how to put it directly into practice due to the combination of the two functions (I have tried playing with liftM2
and iterate
).
The type signatures are as follows:
nextFrameR :: Map -> IO Map
render_ :: Map -> IO Map -- originally Map -> IO ()
I'm not really sure where to go from here, I can do something like:
(iterate (>>= nextFrameR) initialMap) :: [IO Map]
But that just gives me an (infinite?) list of frames (I think), which is great, it just doesn't let me print them as I don't know how to combine the rendering function in there.
iterate
works fairly well for non-IO computation, but if you are in IO
you can't easily exploit iterate
.
To see why, your list
(iterate (>>= nextFrameR) initialMap) :: [IO Map]
is
[ initialMap
, initialMap >>= nextFrameR
, initialMap >>= nextFrameR >>= nextFrameR
...
so... how could we exploit that to have an infinite loop? We can't take the non-existent "last element". We also can't execute all the operations in that list in sequence, since that would run initialMap
many times.
It's easier if we avoid using iterate
and resort to basics like recursion:
loop :: Map -> IO ()
loop m = do
m' <- nextFrameR m
render_ m' -- it looks like you want this
-- feel free to add some delay here, or some stopping condition to exit the loop
loop m'
main :: IO ()
main = do
m <- initialMap
loop m
You can turn the above code into some code which uses >>=
but there is no need to.
Finally, there's no need to make render_
return the same Map
. You can make that return IO ()
.
If you are a beginner, I'd recommend to initially stay away from "smart" library functions like mapM_, traverse, for, sequence, liftM2, ap, ...
and learn to do everything using only do
-blocks and recursion. Then, once you get how this works, you can try to improve your code exploiting the library helpers.