-
Notifications
You must be signed in to change notification settings - Fork 47
Description
I am in the midst of trying to add Generic instances for the above classes.
Rationale
It is my understanding that in order to currently use the library, things such as the following must be written:
newtype Position = Position { getPosition :: V3 Float }
instance Component Position where type Storage Position = Map Position
-- And so on, for say @Velocity and a @PlayerControlled tag...
-- We then define this type alias, which we must ensure smaller than an 9-tuple by nesting eventually.
-- This is undesirable because it is 1. annoying and 2. prone to error upon adding new components to the PlayerVehicle.
type PlayerVehicle = (PlayerControlled, Position, Velocity)
-- Then we use this in a system, using the PlayerVehicle type alias to help ensure correctness after adding new components to PlayerVehicle.
-- This means we have to update everywhere PlayerVehicle is used, even if we don't use the new added component in the system/whatever.
mySystem = cmap (\((tag, pos, vel) :: PlayerVehicle) -> (tag, pos + vel, vel)) -- pretend this makes sense
-- I omitted the newtype dance for brevity.I would prefer to write instead something like the following:
-- UNCHANGED
newtype Position = Position { getPosition :: V3 Float }
instance Component Position where type Storage Position = Map Position
-- And so on, for say @Velocity and a @PlayerControlled tag...
-- CHANGED
data PlayerVehicle = PlayerVehicle { pvTag :: PlayerControlled, pvPos :: Position, pvVel :: Velocity } deriving (Generic)
instance Component PlayerVehicle
-- CHANGED
mySystem = cmap (\(v :: PlayerVehicle) -> v { pvPos = pvPos v + pvVel v }
-- One can imagine making this nicer with lenses, but that is kind of outside the point of this ticket.Notice that adding a new field to PlayerVehicle does not change the implementation of mySystem.
Performance
apecs appears to be heavily performance focused, and I do not wish to introduce runtime overhead when unnecessary.
The proposed changes have some obvious (and probably not so obvious) performance implications, but I believe these are entirely opt-in; benchmarks would need to be performed before I could commit to this statement unqualified.
However, assuming the above point, this change would allow us to design our systems using components that are plain Haskell datatypes, instead of a mess of tuples of length < 9. If we find through benchmarking that some systems need to be optimized, so be it, switch those systems to tuples! But if they don't, then I believe this to be much better than the status quo, in terms of code maintainability and readability.
Work
I am already in the middle of performing the work, and will likely open a PR in the future with the necessary changes (adding G[Component,Elem,Expl[Get,Set,Destroy,Members]] classes/families and instances, and the default signature to the original non-G version of those classes/families). I thought this would be useful outside of my specific case however, and therefore am offering to do the necessary work to include it in apecs proper.