Reputation: 687
I am writing a game program in Haskell that currently has a data type like this
data World = World {
worldPlayer :: !(IORef GameObject),
worldEntities :: ![IORef GameObject],
...
}
Each update, the following update is written to the player IORef
:
updatePlayer :: GameObject -> [IORef GameObject] -> IO GameObject
In this function, it checks for collision on each object, then moves the player. But I would like the updatePlayer function to be pure, so I need to use a different data structure.
The most obvious idea is to take the [IORef GameObject]
from the world and transform it into an IO [GameObject]
by calling readIORef
on each index. But this would be very inefficient.
Another possible way I found to do this is using Data.Vector.MVector
and Data.Vector.Generic.unsafeUnfreeze
and unsafeFreeze
, which have O(1)
performance to do worldEntities :: !(MVector (PrimState IO) GameObject)
. The problem is that unsafeUnfreeze
and unsafeFreeze
only work on certain data types.
I also found IOArray
, so I could use IOArray Int GameObject
, but I cannot find a way to convert IOArray
s into an immutable structure.
Last, I could just do IORef [GameObject]
or IORef (Vector GameObject)
, but I am unsure how efficient this would be.
What is the most efficient way to do this?
Upvotes: 1
Views: 1659
Reputation: 5172
You can use lenses instead of mutable objects, to get "setter-like" behavior. Try that before messing around with mutable state, which is very ugly in Haskell (intentionally ugly, to discourage you from doing it).
(Edit to add: "setter-like" syntax. Lens "setters" still create new references to the "set"ted result, so you still need to sequence your main loop to read from the returned value from the setter, you can't re-read an old (immutable) reference to get an updated value, of course.)
Upvotes: 5