packages feed

ecstasy 0.1.1.1 → 0.2.0.0

raw patch · 7 files changed

+893/−443 lines, 7 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Data.Ecstasy: Ent :: Int -> Ent
- Data.Ecstasy: FieldOf :: StorageType
- Data.Ecstasy: SetterOf :: StorageType
- Data.Ecstasy: WorldOf :: StorageType
- Data.Ecstasy: [unEnt] :: Ent -> Int
- Data.Ecstasy: class HasWorld world
- Data.Ecstasy: convertSetter :: (HasWorld world, GConvertSetter (Rep (world 'FieldOf)) (Rep (world 'SetterOf)), Generic (world 'FieldOf), Generic (world 'SetterOf)) => world 'FieldOf -> world 'SetterOf
- Data.Ecstasy: data StorageType
- Data.Ecstasy: defEntity :: (HasWorld world, Generic (world 'FieldOf), GDefault 'True (Rep (world 'FieldOf))) => world 'FieldOf
- Data.Ecstasy: defEntity' :: (HasWorld world, Generic (world 'SetterOf), GDefault 'True (Rep (world 'SetterOf))) => world 'SetterOf
- Data.Ecstasy: defWorld :: (HasWorld world, Generic (world 'WorldOf), GDefault 'True (Rep (world 'WorldOf))) => world 'WorldOf
- Data.Ecstasy: get :: Monad m => (world 'FieldOf -> Maybe a) -> QueryT world m a
- Data.Ecstasy: getMaybe :: Monad m => (world 'FieldOf -> Maybe a) -> QueryT world m (Maybe a)
- Data.Ecstasy: getWorld :: Monad m => SystemT world m (world 'WorldOf)
- Data.Ecstasy: instance (GHC.Generics.Generic (world 'Data.Ecstasy.Types.SetterOf), GHC.Generics.Generic (world 'Data.Ecstasy.Types.WorldOf), GHC.Generics.Generic (world 'Data.Ecstasy.Types.FieldOf), Data.Ecstasy.Deriving.GSetEntity (GHC.Generics.Rep (world 'Data.Ecstasy.Types.SetterOf)) (GHC.Generics.Rep (world 'Data.Ecstasy.Types.WorldOf)), Data.Ecstasy.Deriving.GGetEntity (GHC.Generics.Rep (world 'Data.Ecstasy.Types.WorldOf)) (GHC.Generics.Rep (world 'Data.Ecstasy.Types.FieldOf)), Data.Ecstasy.Deriving.GConvertSetter (GHC.Generics.Rep (world 'Data.Ecstasy.Types.FieldOf)) (GHC.Generics.Rep (world 'Data.Ecstasy.Types.SetterOf)), Data.Ecstasy.Deriving.GDefault 'GHC.Types.True (GHC.Generics.Rep (world 'Data.Ecstasy.Types.FieldOf)), Data.Ecstasy.Deriving.GDefault 'GHC.Types.False (GHC.Generics.Rep (world 'Data.Ecstasy.Types.SetterOf)), Data.Ecstasy.Deriving.GDefault 'GHC.Types.True (GHC.Generics.Rep (world 'Data.Ecstasy.Types.SetterOf)), Data.Ecstasy.Deriving.GDefault 'GHC.Types.True (GHC.Generics.Rep (world 'Data.Ecstasy.Types.WorldOf))) => Data.Ecstasy.HasWorld world
- Data.Ecstasy: newtype Ent
- Data.Ecstasy: nextEntity :: Monad m => SystemT a m Ent
- Data.Ecstasy: type QueryT w m = ReaderT (w 'FieldOf) (MaybeT m)
- Data.Ecstasy: type SystemT w = StateT (SystemState w)
- Data.Ecstasy: unQueryT :: QueryT world m a -> world 'FieldOf -> m (Maybe a)
+ Data.Ecstasy: VTable :: !(Ent -> m (Maybe a)) -> !(Ent -> Update a -> m ()) -> VTable m a
+ Data.Ecstasy: Virtual :: ComponentType
+ Data.Ecstasy: [vget] :: VTable m a -> !(Ent -> m (Maybe a))
+ Data.Ecstasy: [vset] :: VTable m a -> !(Ent -> Update a -> m ())
+ Data.Ecstasy: allEnts :: Monad m => EntTarget world m
+ Data.Ecstasy: anEnt :: Monad m => Ent -> EntTarget world m
+ Data.Ecstasy: createEntity :: (HasWorld world m, Monad m) => world 'FieldOf -> SystemT world m Ent
+ Data.Ecstasy: data Ent
+ Data.Ecstasy: data QueryT w m a
+ Data.Ecstasy: data SystemT w m a
+ Data.Ecstasy: data VTable m a
+ Data.Ecstasy: defStorage :: HasWorld world m => world ( 'WorldOf m)
+ Data.Ecstasy: eover :: (HasWorld world m, Monad m) => EntTarget world m -> QueryT world m (a, world 'SetterOf) -> SystemT world m [a]
+ Data.Ecstasy: maybeToUpdate :: Maybe a -> Update a
+ Data.Ecstasy: query :: Monad m => (world 'FieldOf -> Maybe a) -> QueryT world m a
+ Data.Ecstasy: queryDef :: Monad m => z -> (world 'FieldOf -> Maybe z) -> QueryT world m z
+ Data.Ecstasy: queryEnt :: Monad m => QueryT world m Ent
+ Data.Ecstasy: queryFlag :: Monad m => (world 'FieldOf -> Maybe ()) -> QueryT world m Bool
+ Data.Ecstasy: queryMaybe :: Monad m => (world 'FieldOf -> Maybe a) -> QueryT world m (Maybe a)
+ Data.Ecstasy: someEnts :: Monad m => [Ent] -> EntTarget world m
+ Data.Ecstasy: type EntTarget world m = SystemT world m [Ent]
+ Data.Ecstasy: unchanged :: HasWorld' world => world 'SetterOf
+ Data.Ecstasy.Internal: allEnts :: Monad m => EntTarget world m
+ Data.Ecstasy.Internal: anEnt :: Monad m => Ent -> EntTarget world m
+ Data.Ecstasy.Internal: class HasWorld' world => HasWorld world m
+ Data.Ecstasy.Internal: class HasWorld' world
+ Data.Ecstasy.Internal: convertSetter :: (HasWorld' world, GConvertSetter (Rep (world 'FieldOf)) (Rep (world 'SetterOf)), Generic (world 'FieldOf), Generic (world 'SetterOf)) => world 'FieldOf -> world 'SetterOf
+ Data.Ecstasy.Internal: createEntity :: (HasWorld world m, Monad m) => world 'FieldOf -> SystemT world m Ent
+ Data.Ecstasy.Internal: defStorage :: (HasWorld world m, Generic (world ( 'WorldOf m)), GDefault 'True (Rep (world ( 'WorldOf m)))) => world ( 'WorldOf m)
+ Data.Ecstasy.Internal: delEntity :: (HasWorld' world, Generic (world 'SetterOf), GDefault 'False (Rep (world 'SetterOf))) => world 'SetterOf
+ Data.Ecstasy.Internal: deleteEntity :: (HasWorld world m, Monad m) => Ent -> SystemT world m ()
+ Data.Ecstasy.Internal: efor :: (HasWorld world m, Monad m) => EntTarget world m -> QueryT world m a -> SystemT world m [a]
+ Data.Ecstasy.Internal: emap :: (HasWorld world m, Monad m) => EntTarget world m -> QueryT world m (world 'SetterOf) -> SystemT world m ()
+ Data.Ecstasy.Internal: eover :: (HasWorld world m, Monad m) => EntTarget world m -> QueryT world m (a, world 'SetterOf) -> SystemT world m [a]
+ Data.Ecstasy.Internal: getEntity :: (HasWorld world m, Monad m, GGetEntity m (Rep (world ( 'WorldOf m))) (Rep (world 'FieldOf)), Generic (world 'FieldOf), Generic (world ( 'WorldOf m))) => Ent -> SystemT world m (world 'FieldOf)
+ Data.Ecstasy.Internal: instance (Data.Ecstasy.Internal.HasWorld' world, GHC.Generics.Generic (world 'Data.Ecstasy.Types.SetterOf), GHC.Generics.Generic (world ('Data.Ecstasy.Types.WorldOf m)), GHC.Generics.Generic (world 'Data.Ecstasy.Types.FieldOf), Data.Ecstasy.Internal.Deriving.GConvertSetter (GHC.Generics.Rep (world 'Data.Ecstasy.Types.FieldOf)) (GHC.Generics.Rep (world 'Data.Ecstasy.Types.SetterOf)), Data.Ecstasy.Internal.Deriving.GDefault 'GHC.Types.True (GHC.Generics.Rep (world 'Data.Ecstasy.Types.FieldOf)), Data.Ecstasy.Internal.Deriving.GDefault 'GHC.Types.False (GHC.Generics.Rep (world 'Data.Ecstasy.Types.SetterOf)), Data.Ecstasy.Internal.Deriving.GDefault 'GHC.Types.True (GHC.Generics.Rep (world 'Data.Ecstasy.Types.SetterOf)), Data.Ecstasy.Internal.Deriving.GDefault 'GHC.Types.True (GHC.Generics.Rep (world ('Data.Ecstasy.Types.WorldOf m))), Data.Ecstasy.Internal.Deriving.GSetEntity m (GHC.Generics.Rep (world 'Data.Ecstasy.Types.SetterOf)) (GHC.Generics.Rep (world ('Data.Ecstasy.Types.WorldOf m))), Data.Ecstasy.Internal.Deriving.GGetEntity m (GHC.Generics.Rep (world ('Data.Ecstasy.Types.WorldOf m))) (GHC.Generics.Rep (world 'Data.Ecstasy.Types.FieldOf)), GHC.Base.Monad m) => Data.Ecstasy.Internal.HasWorld world m
+ Data.Ecstasy.Internal: instance (GHC.Generics.Generic (world 'Data.Ecstasy.Types.SetterOf), GHC.Generics.Generic (world 'Data.Ecstasy.Types.FieldOf), Data.Ecstasy.Internal.Deriving.GConvertSetter (GHC.Generics.Rep (world 'Data.Ecstasy.Types.FieldOf)) (GHC.Generics.Rep (world 'Data.Ecstasy.Types.SetterOf)), Data.Ecstasy.Internal.Deriving.GDefault 'GHC.Types.True (GHC.Generics.Rep (world 'Data.Ecstasy.Types.FieldOf)), Data.Ecstasy.Internal.Deriving.GDefault 'GHC.Types.False (GHC.Generics.Rep (world 'Data.Ecstasy.Types.SetterOf)), Data.Ecstasy.Internal.Deriving.GDefault 'GHC.Types.True (GHC.Generics.Rep (world 'Data.Ecstasy.Types.SetterOf))) => Data.Ecstasy.Internal.HasWorld' world
+ Data.Ecstasy.Internal: maybeToUpdate :: Maybe a -> Update a
+ Data.Ecstasy.Internal: newEntity :: (HasWorld' world, Generic (world 'FieldOf), GDefault 'True (Rep (world 'FieldOf))) => world 'FieldOf
+ Data.Ecstasy.Internal: nextEntity :: Monad m => SystemT a m Ent
+ Data.Ecstasy.Internal: query :: Monad m => (world 'FieldOf -> Maybe a) -> QueryT world m a
+ Data.Ecstasy.Internal: queryDef :: Monad m => z -> (world 'FieldOf -> Maybe z) -> QueryT world m z
+ Data.Ecstasy.Internal: queryEnt :: Monad m => QueryT world m Ent
+ Data.Ecstasy.Internal: queryFlag :: Monad m => (world 'FieldOf -> Maybe ()) -> QueryT world m Bool
+ Data.Ecstasy.Internal: queryMaybe :: Monad m => (world 'FieldOf -> Maybe a) -> QueryT world m (Maybe a)
+ Data.Ecstasy.Internal: runQueryT :: (HasWorld world m, Monad m) => Ent -> QueryT world m a -> SystemT world m (Maybe a)
+ Data.Ecstasy.Internal: runSystem :: world ( 'WorldOf Identity) -> System world a -> a
+ Data.Ecstasy.Internal: runSystemT :: Monad m => world ( 'WorldOf m) -> SystemT world m a -> m a
+ Data.Ecstasy.Internal: setEntity :: (HasWorld world m, GSetEntity m (Rep (world 'SetterOf)) (Rep (world ( 'WorldOf m))), Generic (world ( 'WorldOf m)), Generic (world 'SetterOf), Monad m) => Ent -> world 'SetterOf -> SystemT world m ()
+ Data.Ecstasy.Internal: someEnts :: Monad m => [Ent] -> EntTarget world m
+ Data.Ecstasy.Internal: type EntTarget world m = SystemT world m [Ent]
+ Data.Ecstasy.Internal: unQueryT :: QueryT world m a -> Ent -> world 'FieldOf -> m (Maybe a)
+ Data.Ecstasy.Internal: unchanged :: (HasWorld' world, Generic (world 'SetterOf), GDefault 'True (Rep (world 'SetterOf))) => world 'SetterOf
+ Data.Ecstasy.Internal: with :: Monad m => (world 'FieldOf -> Maybe a) -> QueryT world m ()
+ Data.Ecstasy.Internal: without :: Monad m => (world 'FieldOf -> Maybe a) -> QueryT world m ()
+ Data.Ecstasy.Internal: yieldSystemT :: Monad m => SystemState world m -> SystemT world m a -> m (SystemState world m, a)
+ Data.Ecstasy.Internal.Deriving: class GConvertSetter a b
+ Data.Ecstasy.Internal.Deriving: class GDefault (keep :: Bool) f
+ Data.Ecstasy.Internal.Deriving: class GGetEntity m a b
+ Data.Ecstasy.Internal.Deriving: class GSetEntity m a b
+ Data.Ecstasy.Internal.Deriving: def :: forall keep a. (Generic a, GDefault keep (Rep a)) => a
+ Data.Ecstasy.Internal.Deriving: gConvertSetter :: GConvertSetter a b => a x -> b x
+ Data.Ecstasy.Internal.Deriving: gGetEntity :: GGetEntity m a b => a x -> Int -> m (b x)
+ Data.Ecstasy.Internal.Deriving: gSetEntity :: GSetEntity m a b => a x -> Int -> b x -> m (b x)
+ Data.Ecstasy.Internal.Deriving: gdef :: GDefault keep f => f a
+ Data.Ecstasy.Internal.Deriving: instance (Data.Ecstasy.Internal.Deriving.GConvertSetter a c, Data.Ecstasy.Internal.Deriving.GConvertSetter b d) => Data.Ecstasy.Internal.Deriving.GConvertSetter (a GHC.Generics.:*: b) (c GHC.Generics.:*: d)
+ Data.Ecstasy.Internal.Deriving: instance (Data.Ecstasy.Internal.Deriving.GDefault keep a, Data.Ecstasy.Internal.Deriving.GDefault keep b) => Data.Ecstasy.Internal.Deriving.GDefault keep (a GHC.Generics.:*: b)
+ Data.Ecstasy.Internal.Deriving: instance (GHC.Base.Applicative m, Data.Ecstasy.Internal.Deriving.GGetEntity m a c, Data.Ecstasy.Internal.Deriving.GGetEntity m b d) => Data.Ecstasy.Internal.Deriving.GGetEntity m (a GHC.Generics.:*: b) (c GHC.Generics.:*: d)
+ Data.Ecstasy.Internal.Deriving: instance (GHC.Base.Applicative m, Data.Ecstasy.Internal.Deriving.GSetEntity m a c, Data.Ecstasy.Internal.Deriving.GSetEntity m b d) => Data.Ecstasy.Internal.Deriving.GSetEntity m (a GHC.Generics.:*: b) (c GHC.Generics.:*: d)
+ Data.Ecstasy.Internal.Deriving: instance (GHC.Base.Applicative m, GHC.TypeLits.KnownSymbol sym) => Data.Ecstasy.Internal.Deriving.GDefault keep (GHC.Generics.M1 GHC.Generics.S ('GHC.Generics.MetaSel ('GHC.Base.Just sym) x y z) (GHC.Generics.K1 i (Data.Ecstasy.Types.VTable m a)))
+ Data.Ecstasy.Internal.Deriving: instance (GHC.Base.Functor m, Data.Ecstasy.Internal.Deriving.GGetEntity m f f') => Data.Ecstasy.Internal.Deriving.GGetEntity m (GHC.Generics.M1 i c f) (GHC.Generics.M1 i' c' f')
+ Data.Ecstasy.Internal.Deriving: instance (GHC.Base.Functor m, Data.Ecstasy.Internal.Deriving.GSetEntity m f f') => Data.Ecstasy.Internal.Deriving.GSetEntity m (GHC.Generics.M1 i c f) (GHC.Generics.M1 i' c' f')
+ Data.Ecstasy.Internal.Deriving: instance Data.Ecstasy.Internal.Deriving.GConvertSetter (GHC.Generics.K1 i (GHC.Base.Maybe a)) (GHC.Generics.K1 i' (Data.Ecstasy.Types.Update a))
+ Data.Ecstasy.Internal.Deriving: instance Data.Ecstasy.Internal.Deriving.GConvertSetter (GHC.Generics.K1 i a) (GHC.Generics.K1 i' (Data.Ecstasy.Types.Update a))
+ Data.Ecstasy.Internal.Deriving: instance Data.Ecstasy.Internal.Deriving.GConvertSetter (GHC.Generics.K1 i a) (GHC.Generics.K1 i' (GHC.Base.Maybe a))
+ Data.Ecstasy.Internal.Deriving: instance Data.Ecstasy.Internal.Deriving.GConvertSetter f f' => Data.Ecstasy.Internal.Deriving.GConvertSetter (GHC.Generics.M1 i c f) (GHC.Generics.M1 i' c' f')
+ Data.Ecstasy.Internal.Deriving: instance Data.Ecstasy.Internal.Deriving.GDefault 'GHC.Types.False (GHC.Generics.K1 i (Data.Ecstasy.Types.Update c))
+ Data.Ecstasy.Internal.Deriving: instance Data.Ecstasy.Internal.Deriving.GDefault 'GHC.Types.True (GHC.Generics.K1 i (Data.Ecstasy.Types.Update c))
+ Data.Ecstasy.Internal.Deriving: instance Data.Ecstasy.Internal.Deriving.GDefault keep (GHC.Generics.K1 i (Data.IntMap.Internal.IntMap c))
+ Data.Ecstasy.Internal.Deriving: instance Data.Ecstasy.Internal.Deriving.GDefault keep (GHC.Generics.K1 i (GHC.Base.Maybe c))
+ Data.Ecstasy.Internal.Deriving: instance Data.Ecstasy.Internal.Deriving.GDefault keep GHC.Generics.U1
+ Data.Ecstasy.Internal.Deriving: instance Data.Ecstasy.Internal.Deriving.GDefault keep f => Data.Ecstasy.Internal.Deriving.GDefault keep (GHC.Generics.M1 i c f)
+ Data.Ecstasy.Internal.Deriving: instance GHC.Base.Applicative m => Data.Ecstasy.Internal.Deriving.GGetEntity m (GHC.Generics.K1 i (Data.Ecstasy.Types.VTable m a)) (GHC.Generics.K1 i' (GHC.Base.Maybe a))
+ Data.Ecstasy.Internal.Deriving: instance GHC.Base.Applicative m => Data.Ecstasy.Internal.Deriving.GGetEntity m (GHC.Generics.K1 i (Data.IntMap.Internal.IntMap a)) (GHC.Generics.K1 i' (GHC.Base.Maybe a))
+ Data.Ecstasy.Internal.Deriving: instance GHC.Base.Applicative m => Data.Ecstasy.Internal.Deriving.GGetEntity m (GHC.Generics.K1 i (GHC.Base.Maybe (GHC.Types.Int, a))) (GHC.Generics.K1 i' (GHC.Base.Maybe a))
+ Data.Ecstasy.Internal.Deriving: instance GHC.Base.Applicative m => Data.Ecstasy.Internal.Deriving.GSetEntity m (GHC.Generics.K1 i (Data.Ecstasy.Types.Update a)) (GHC.Generics.K1 i' (Data.Ecstasy.Types.VTable m a))
+ Data.Ecstasy.Internal.Deriving: instance GHC.Base.Applicative m => Data.Ecstasy.Internal.Deriving.GSetEntity m (GHC.Generics.K1 i (Data.Ecstasy.Types.Update a)) (GHC.Generics.K1 i' (Data.IntMap.Internal.IntMap a))
+ Data.Ecstasy.Internal.Deriving: instance GHC.Base.Applicative m => Data.Ecstasy.Internal.Deriving.GSetEntity m (GHC.Generics.K1 i (Data.Ecstasy.Types.Update a)) (GHC.Generics.K1 i' (GHC.Base.Maybe (GHC.Types.Int, a)))
+ Data.Ecstasy.Types: Ent :: Int -> Ent
+ Data.Ecstasy.Types: Field :: ComponentType
+ Data.Ecstasy.Types: FieldOf :: StorageType
+ Data.Ecstasy.Types: Keep :: Update a
+ Data.Ecstasy.Types: QueryT :: ReaderT (Ent, w 'FieldOf) (MaybeT m) a -> QueryT w m a
+ Data.Ecstasy.Types: Set :: !a -> Update a
+ Data.Ecstasy.Types: SetterOf :: StorageType
+ Data.Ecstasy.Types: SystemT :: StateT (SystemState w m) m a -> SystemT w m a
+ Data.Ecstasy.Types: Unique :: ComponentType
+ Data.Ecstasy.Types: Unset :: Update a
+ Data.Ecstasy.Types: VTable :: !(Ent -> m (Maybe a)) -> !(Ent -> Update a -> m ()) -> VTable m a
+ Data.Ecstasy.Types: Virtual :: ComponentType
+ Data.Ecstasy.Types: WorldOf :: (Type -> Type) -> StorageType
+ Data.Ecstasy.Types: [runQueryT'] :: QueryT w m a -> ReaderT (Ent, w 'FieldOf) (MaybeT m) a
+ Data.Ecstasy.Types: [runSystemT'] :: SystemT w m a -> StateT (SystemState w m) m a
+ Data.Ecstasy.Types: [unEnt] :: Ent -> Int
+ Data.Ecstasy.Types: [vget] :: VTable m a -> !(Ent -> m (Maybe a))
+ Data.Ecstasy.Types: [vset] :: VTable m a -> !(Ent -> Update a -> m ())
+ Data.Ecstasy.Types: data ComponentType
+ Data.Ecstasy.Types: data StorageType
+ Data.Ecstasy.Types: data Update a
+ Data.Ecstasy.Types: data VTable m a
+ Data.Ecstasy.Types: instance Control.Monad.IO.Class.MonadIO m => Control.Monad.IO.Class.MonadIO (Data.Ecstasy.Types.QueryT w m)
+ Data.Ecstasy.Types: instance Control.Monad.IO.Class.MonadIO m => Control.Monad.IO.Class.MonadIO (Data.Ecstasy.Types.SystemT w m)
+ Data.Ecstasy.Types: instance Control.Monad.Reader.Class.MonadReader r m => Control.Monad.Reader.Class.MonadReader r (Data.Ecstasy.Types.QueryT w m)
+ Data.Ecstasy.Types: instance Control.Monad.Reader.Class.MonadReader r m => Control.Monad.Reader.Class.MonadReader r (Data.Ecstasy.Types.SystemT w m)
+ Data.Ecstasy.Types: instance Control.Monad.State.Class.MonadState s m => Control.Monad.State.Class.MonadState s (Data.Ecstasy.Types.QueryT w m)
+ Data.Ecstasy.Types: instance Control.Monad.State.Class.MonadState s m => Control.Monad.State.Class.MonadState s (Data.Ecstasy.Types.SystemT w m)
+ Data.Ecstasy.Types: instance Control.Monad.Trans.Class.MonadTrans (Data.Ecstasy.Types.QueryT w)
+ Data.Ecstasy.Types: instance Control.Monad.Trans.Class.MonadTrans (Data.Ecstasy.Types.SystemT w)
+ Data.Ecstasy.Types: instance Control.Monad.Writer.Class.MonadWriter ww m => Control.Monad.Writer.Class.MonadWriter ww (Data.Ecstasy.Types.QueryT w m)
+ Data.Ecstasy.Types: instance Control.Monad.Writer.Class.MonadWriter ww m => Control.Monad.Writer.Class.MonadWriter ww (Data.Ecstasy.Types.SystemT w m)
+ Data.Ecstasy.Types: instance Data.Foldable.Foldable Data.Ecstasy.Types.Update
+ Data.Ecstasy.Types: instance Data.Traversable.Traversable Data.Ecstasy.Types.Update
+ Data.Ecstasy.Types: instance GHC.Base.Functor Data.Ecstasy.Types.Update
+ Data.Ecstasy.Types: instance GHC.Base.Functor m => GHC.Base.Functor (Data.Ecstasy.Types.QueryT w m)
+ Data.Ecstasy.Types: instance GHC.Base.Functor m => GHC.Base.Functor (Data.Ecstasy.Types.SystemT w m)
+ Data.Ecstasy.Types: instance GHC.Base.Monad m => GHC.Base.Alternative (Data.Ecstasy.Types.QueryT w m)
+ Data.Ecstasy.Types: instance GHC.Base.Monad m => GHC.Base.Applicative (Data.Ecstasy.Types.QueryT w m)
+ Data.Ecstasy.Types: instance GHC.Base.Monad m => GHC.Base.Applicative (Data.Ecstasy.Types.SystemT w m)
+ Data.Ecstasy.Types: instance GHC.Base.Monad m => GHC.Base.Monad (Data.Ecstasy.Types.QueryT w m)
+ Data.Ecstasy.Types: instance GHC.Base.Monad m => GHC.Base.Monad (Data.Ecstasy.Types.SystemT w m)
+ Data.Ecstasy.Types: instance GHC.Base.Monad m => GHC.Base.MonadPlus (Data.Ecstasy.Types.QueryT w m)
+ Data.Ecstasy.Types: instance GHC.Classes.Eq Data.Ecstasy.Types.Ent
+ Data.Ecstasy.Types: instance GHC.Classes.Eq a => GHC.Classes.Eq (Data.Ecstasy.Types.Update a)
+ Data.Ecstasy.Types: instance GHC.Classes.Ord Data.Ecstasy.Types.Ent
+ Data.Ecstasy.Types: instance GHC.Classes.Ord a => GHC.Classes.Ord (Data.Ecstasy.Types.Update a)
+ Data.Ecstasy.Types: instance GHC.Read.Read a => GHC.Read.Read (Data.Ecstasy.Types.Update a)
+ Data.Ecstasy.Types: instance GHC.Show.Show Data.Ecstasy.Types.Ent
+ Data.Ecstasy.Types: instance GHC.Show.Show a => GHC.Show.Show (Data.Ecstasy.Types.Update a)
+ Data.Ecstasy.Types: newtype Ent
+ Data.Ecstasy.Types: newtype QueryT w m a
+ Data.Ecstasy.Types: newtype SystemT w m a
+ Data.Ecstasy.Types: type System w = SystemT w Identity
+ Data.Ecstasy.Types: type SystemState w m = (Int, w ( 'WorldOf m))
- Data.Ecstasy: Set :: a -> Update a
+ Data.Ecstasy: Set :: !a -> Update a
- Data.Ecstasy: delEntity :: (HasWorld world, Generic (world 'SetterOf), GDefault 'False (Rep (world 'SetterOf))) => world 'SetterOf
+ Data.Ecstasy: delEntity :: HasWorld' world => world 'SetterOf
- Data.Ecstasy: deleteEntity :: (HasWorld world, Monad m) => Ent -> SystemT world m ()
+ Data.Ecstasy: deleteEntity :: (HasWorld world m, Monad m) => Ent -> SystemT world m ()
- Data.Ecstasy: efor :: (HasWorld world, Monad m) => (Ent -> QueryT world m a) -> SystemT world m [a]
+ Data.Ecstasy: efor :: (HasWorld world m, Monad m) => EntTarget world m -> QueryT world m a -> SystemT world m [a]
- Data.Ecstasy: emap :: (HasWorld world, Monad m) => QueryT world m (world 'SetterOf) -> SystemT world m ()
+ Data.Ecstasy: emap :: (HasWorld world m, Monad m) => EntTarget world m -> QueryT world m (world 'SetterOf) -> SystemT world m ()
- Data.Ecstasy: getEntity :: (HasWorld world, GGetEntity (Rep (world 'WorldOf)) (Rep (world 'FieldOf)), Generic (world 'FieldOf), Generic (world 'WorldOf), Monad m) => Ent -> SystemT world m (world 'FieldOf)
+ Data.Ecstasy: getEntity :: (HasWorld world m, Monad m) => Ent -> SystemT world m (world 'FieldOf)
- Data.Ecstasy: newEntity :: (HasWorld world, Monad m) => world 'FieldOf -> SystemT world m Ent
+ Data.Ecstasy: newEntity :: HasWorld' world => world 'FieldOf
- Data.Ecstasy: runQueryT :: (HasWorld world, Monad m) => Ent -> QueryT world m a -> SystemT world m (Maybe a)
+ Data.Ecstasy: runQueryT :: (HasWorld world m, Monad m) => Ent -> QueryT world m a -> SystemT world m (Maybe a)
- Data.Ecstasy: runSystem :: world 'WorldOf -> System world a -> a
+ Data.Ecstasy: runSystem :: world ( 'WorldOf Identity) -> System world a -> a
- Data.Ecstasy: runSystemT :: Monad m => world 'WorldOf -> SystemT world m a -> m a
+ Data.Ecstasy: runSystemT :: Monad m => world ( 'WorldOf m) -> SystemT world m a -> m a
- Data.Ecstasy: setEntity :: (HasWorld world, GSetEntity (Rep (world 'SetterOf)) (Rep (world 'WorldOf)), Generic (world 'WorldOf), Generic (world 'SetterOf), Monad m) => Ent -> world 'SetterOf -> SystemT world m ()
+ Data.Ecstasy: setEntity :: HasWorld world m => Ent -> world 'SetterOf -> SystemT world m ()
- Data.Ecstasy: type SystemState w = (Int, w 'WorldOf)
+ Data.Ecstasy: type SystemState w m = (Int, w ( 'WorldOf m))
- Data.Ecstasy: yieldSystemT :: Monad m => SystemState world -> SystemT world m a -> m (SystemState world, a)
+ Data.Ecstasy: yieldSystemT :: Monad m => SystemState world m -> SystemT world m a -> m (SystemState world m, a)

Files

ChangeLog.md view
@@ -1,8 +1,23 @@ # Revision history for ecstasy -## 0.1.1.1  -- 2018-05-17+## 0.2.0.0  -- 2018-05-10 -* Use strict datastructures to avoid memory leaks. Backported from 0.2 branch.+* Renamed 'get*' to 'query*'.+* Renamed 'newEntity' to 'createEntity'.+* Renamed 'defEntity' to 'newEntity'.+* Renamed 'defEntity'' to 'unchanged'.+* Renamed 'defWorld' to 'defStorage'.+* Significant performance improvements.+* Added a 'Virtual' component type, allowing for easy integration with systems+    that own their own data. Getting and setting on 'Virtual' components+    dispatch as actions in the underlying monad stack.+* Added proper type wrappers around 'SystemT' and 'QueryT' so they don't eat up+    valuable mtl instances.+* Removed the 'Ent' parameter from the 'efor' callback, since this can now be+    gotten in any 'QueryT' context via 'queryEnt'.+* Parameterized 'emap' and 'efor' by an 'EntityTarget', which allows for calling+    these functions over specific groups of entities.+* Added 'eover': a combination of 'emap' and 'efor'.  ## 0.1.1.0  -- 2018-02-18 
ecstasy.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/  name:                ecstasy-version:             0.1.1.1+version:             0.2.0.0 synopsis:   A GHC.Generics based entity component system. @@ -35,8 +35,9 @@  library   exposed-modules: Data.Ecstasy-  other-modules:   Data.Ecstasy.Deriving                  , Data.Ecstasy.Types+                 , Data.Ecstasy.Internal+                 , Data.Ecstasy.Internal.Deriving   -- other-extensions:   build-depends:       base >=4.9 && <5, containers, mtl, transformers   hs-source-dirs:      src
src/Data/Ecstasy.hs view
@@ -1,323 +1,248 @@-{-# LANGUAGE DataKinds            #-}-{-# LANGUAGE DefaultSignatures    #-}-{-# LANGUAGE FlexibleContexts     #-}-{-# LANGUAGE FlexibleInstances    #-}-{-# LANGUAGE TupleSections        #-}-{-# LANGUAGE TypeApplications     #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE ViewPatterns         #-}-+-- | Ecstasy is a library architected around the+-- <http://reasonablypolymorphic.com/blog/higher-kinded-data/ HKD pattern>, the+-- gist of which is to define a "template" type that can be reused for several+-- purposes. Users of ecstasy should define a record type of 'Component's+-- parameterized over a variable of kind 'StorageType':+--+-- @+--   data World s = Entity+--     { position :: 'Component' s ''Field' (V2 Double)+--     , graphics :: 'Component' s ''Field' Graphics+--     , isPlayer :: 'Component' s ''Unique' ()+--     }+--     deriving ('Generic')+-- @+--+-- Ensure that this type have an instance of 'Generic'.+--+-- For usability, it might be desirable to also define the following type+-- synonym:+--+-- @+--   type Entity = World 'FieldOf+-- @+--+-- which is the only form of the @World@ that most users of ecstasy will+-- need to interact with.+--+-- Throughout this document there are references to the @HasWorld@ and+-- @HasWorld'@ classes, which are implementation details and provided+-- automatically by the library. module Data.Ecstasy-  ( module Data.Ecstasy-  , module Data.Ecstasy.Types-  , Generic-  ) where+  (+  -- * Defining components+  -- $components+    ComponentType (..)+  , Component -import           Control.Arrow (first, second)-import           Control.Monad (mzero, void)-import           Control.Monad.Trans.Class (lift)-import           Control.Monad.Trans.Maybe (runMaybeT)-import           Control.Monad.Trans.Reader (runReaderT, ask)-import           Control.Monad.Trans.State.Strict (modify, gets, evalStateT)-import qualified Control.Monad.Trans.State.Strict as S-import           Data.Ecstasy.Deriving-import qualified Data.Ecstasy.Types as T-import           Data.Ecstasy.Types hiding (unEnt)-import           Data.Foldable (for_)-import           Data.Functor.Identity (runIdentity)-import           Data.Maybe (catMaybes)-import           Data.Traversable (for)-import           Data.Tuple (swap)-import           GHC.Generics+  -- * Storage+  -- $world+  , defStorage +  -- * The SystemT monad+  -- $systemt+  , SystemT ()+  , runSystemT+  , yieldSystemT+  , System+  , runSystem+  , SystemState ---------------------------------------------------------------------------------- | This class provides all of the functionality necessary to manipulate the--- ECS.-class HasWorld world where+  -- * Working with SystemT+  , createEntity+  , newEntity+  , getEntity+  , setEntity+  , deleteEntity -  -----------------------------------------------------------------------------  -- | Fetches an entity from the world given its 'Ent'.-  getEntity-      :: ( Monad m-         )-      => Ent-      -> SystemT world m (world 'FieldOf)-  default getEntity-      :: ( GGetEntity (Rep (world 'WorldOf))-                      (Rep (world 'FieldOf))-         , Generic (world 'FieldOf)-         , Generic (world 'WorldOf)-         , Monad m-         )-      => Ent-      -> SystemT world m (world 'FieldOf)-  getEntity e = do-    w <- gets snd-    pure . to . gGetEntity (from w) $ T.unEnt e+  -- * SystemT traversals+  -- $traversals+  , emap+  , efor+  , eover+  , unchanged+  , delEntity -  -----------------------------------------------------------------------------  -- | Updates an 'Ent' in the world given its setter.-  setEntity-      :: Monad m-      => Ent-      -> world 'SetterOf-      -> SystemT world m ()-  default setEntity-      :: ( GSetEntity (Rep (world 'SetterOf))-                      (Rep (world 'WorldOf))-         , Generic (world 'WorldOf)-         , Generic (world 'SetterOf)-         , Monad m-         )-      => Ent-      -> world 'SetterOf-      -> SystemT world m ()-  setEntity e s = do-    w <- gets snd-    let x = to . gSetEntity (from s) (T.unEnt e) $ from w-    modify . second $ const x+  -- * Entity targets+  , EntTarget+  , allEnts+  , someEnts+  , anEnt -  -----------------------------------------------------------------------------  -- | Transforms an entity into a setter to transform the default entity into-  -- the given one. Used by 'newEntity'.-  convertSetter-      :: world 'FieldOf-      -> world 'SetterOf-  default convertSetter-      :: ( GConvertSetter (Rep (world 'FieldOf))-                          (Rep (world 'SetterOf))-         , Generic (world 'FieldOf)-         , Generic (world 'SetterOf)-         )-      => world 'FieldOf-      -> world 'SetterOf-  convertSetter = to . gConvertSetter . from-  {-# INLINE convertSetter #-}+  -- * The QueryT monad+  -- $queryt+  , QueryT ()+  , runQueryT -  -----------------------------------------------------------------------------  -- | The default entity, owning no components.-  defEntity :: world 'FieldOf-  default defEntity-      :: ( Generic (world 'FieldOf)-         , GDefault 'True (Rep (world 'FieldOf))-         )-      => world 'FieldOf-  defEntity = def @'True-  {-# INLINE defEntity #-}+  -- * Queries+  -- $querying+  , query+  , with+  , without+  , queryEnt+  , queryMaybe+  , queryFlag+  , queryDef -  -----------------------------------------------------------------------------  -- | The default setter, which keeps all components with their previous value.-  defEntity' :: world 'SetterOf-  default defEntity'-      :: ( Generic (world 'SetterOf)-         , GDefault 'True (Rep (world 'SetterOf))-         )-      => world 'SetterOf-  defEntity' = def @'True-  {-# INLINE defEntity' #-}+  -- * Updates+  , Update (..)+  , maybeToUpdate -  -----------------------------------------------------------------------------  -- | A setter which will delete the entity if its 'QueryT' matches.-  delEntity :: world 'SetterOf-  default delEntity-      :: ( Generic (world 'SetterOf)-         , GDefault 'False (Rep (world 'SetterOf))-         )-      => world 'SetterOf-  delEntity = def @'False-  {-# INLINE delEntity #-}+  -- * Miscellany+  , Ent ()+  , VTable (..) -  -----------------------------------------------------------------------------  -- | The default world, which contains only empty containers.-  defWorld :: world 'WorldOf-  default defWorld-      :: ( Generic (world 'WorldOf)-         , GDefault 'True (Rep (world 'WorldOf))-         )-      => world 'WorldOf-  defWorld = def @'True-  {-# INLINE defWorld #-}+  -- * Re-exports+  , Generic ()+  ) where +import Data.Ecstasy.Internal hiding (HasWorld, HasWorld')+import Data.Ecstasy.Types+import GHC.Generics -instance ( Generic (world 'SetterOf)-         , Generic (world 'WorldOf)-         , Generic (world 'FieldOf)-         , GSetEntity (Rep (world 'SetterOf))-                      (Rep (world 'WorldOf))-         , GGetEntity (Rep (world 'WorldOf))-                      (Rep (world 'FieldOf))-         , GConvertSetter (Rep (world 'FieldOf))-                          (Rep (world 'SetterOf))-         , GDefault 'True  (Rep (world 'FieldOf))-         , GDefault 'False (Rep (world 'SetterOf))-         , GDefault 'True  (Rep (world 'SetterOf))-         , GDefault 'True  (Rep (world 'WorldOf))-         ) => HasWorld world  ---------------------------------------------------------------------------------- | Retrieve a unique 'Ent'.-nextEntity-    :: Monad m-    => SystemT a m Ent-nextEntity = do-  (e, _) <- S.get-  modify . first . const $ e + 1-  pure $ Ent e+-- $components+-- Components are pieces of data that may or may not exist on a particular+-- entity. In fact, an 'Ent' is nothing more than an identifier, against which+-- components are linked.+--+-- Components classified by their 'ComponentType', which describes the+-- semantics behind a component.+--+-- [@Field@]  A 'Field' is a "normal" component and corresponds exactly to+-- a 'Maybe' value.+--+-- [@Unique@] A 'Unique' component may only exist on a single entity at a given+-- time. They are often used to annotate "notable" entites, such as whom the+-- camera should be following.+--+-- [@Virtual@] A 'Virtual' component is defined in terms of monadic 'vget' and+-- 'vset' actions, rather than having dedicated storage in the ECS. Virtual+-- components are often used to connect to external systems, eg. to a 3rd party+-- physics engine which wants to own its own data. For more information on+-- using virtual components, see <#world defStorage>.+--+--  ---------------------------------------------------------------------------------- | Create a new entity.-newEntity-    :: (HasWorld world, Monad m)-    => world 'FieldOf-    -> SystemT world m Ent-newEntity cs = do-  e <- nextEntity-  setEntity e $ convertSetter cs-  pure e +-- $world+-- #world# 'defStorage' provides a suitable container for storing entity data, to+-- be used with 'runSystemT' and friends. If you are not using any 'Virtual'+-- components, it can be used directly.+--+-- However, when using 'Virtual' components, the 'VTable' for each must be set+-- on 'defStorage' before being given as a parameter to 'runSystemT'. For+-- example, we can write a virtual 'String' component that writes its updates+-- to stdout:+--+-- @+--   data World s = Entity+--     { stdout :: 'Component' s ''Virtual' String+--     }+--     deriving ('Generic')+--+--  main :: IO ()+--  main = do+--    let storage = 'defStorage'+--          { stdout = 'VTable'+--              { 'vget' = \\_   -> pure Nothing+--              , 'vset' = \\_ m -> for_ m putStrLn+--              }+--          }+--    'runSystemT' storage $ do+--      void $ 'createEntity' 'unchanged+--        { stdout = Just "hello world"+--        }+-- @+--+-- In this example, if you were to use 'defStorage' rather than @storage@ as the+-- argument to 'runSystemT', you would receive the following error:+--+-- @unset VTable for Virtual component \'stdout\'@ ---------------------------------------------------------------------------------- | Delete an entity.-deleteEntity-    :: (HasWorld world, Monad m)-    => Ent-    -> SystemT world m ()-deleteEntity = flip setEntity delEntity  ---------------------------------------------------------------------------------- | Evaluate a 'QueryT'.-unQueryT-  :: QueryT world m a-  -> world 'FieldOf-  -> m (Maybe a)-unQueryT = (runMaybeT .) . runReaderT+-- $systemt+-- The 'SystemT' transformer provides capabilities for creating, modifying,+-- reading and deleting entities, as well as performing <#traversals query+-- traversals> over them. It is the main monad of ecstasy.  ---------------------------------------------------------------------------------- | Map a 'QueryT' transformation over all entites that match it.-emap-    :: ( HasWorld world-       , Monad m-       )-    => QueryT world m (world 'SetterOf)-    -> SystemT world m ()-emap f = do-  (es, _) <- S.get-  for_ [0 .. es - 1] $ \(Ent -> e) -> do-    cs <- getEntity e-    sets <- lift $ unQueryT f cs-    for_ sets $ setEntity e +-- $queryt+-- The 'QueryT' transformer provides an environment for <#traversals querying>+-- components of an entity. Due to its 'Control.Monad.MonadPlus' instance,+-- failing queries will prevent further computations in the monad from running. ---------------------------------------------------------------------------------- | Collect the results of a monadic computation over every entity matching--- a 'QueryT'.-efor-    :: ( HasWorld world-       , Monad m-       )-    => (Ent -> QueryT world m a)-    -> SystemT world m [a]-efor f = do-  (es, _) <- S.get-  fmap catMaybes $ for [0 .. es - 1] $ \(Ent -> e) -> do-    cs <- getEntity e-    lift $ unQueryT (f e) cs  ---------------------------------------------------------------------------------- | Run a 'QueryT' over a particular 'Ent'.-runQueryT-    :: ( HasWorld world-       , Monad m-       )-    => Ent-    -> QueryT world m a-    -> SystemT world m (Maybe a)-runQueryT e qt = do-  cs <- getEntity e-  lift $ unQueryT qt cs+-- $traversals+-- #traversals# 'SystemT' provides functionality for traversing over entities+-- that match a 'EntTarget' and a <#querying query>. The functions 'emap' and+-- 'eover' return a @world 'SetterOf@, corresponding to partial update of the+-- targeted entity.+--+-- A @world 'SetterOf@ is the world record where all of its selectors have the+-- type @'Update' a@. For example, given a world:+--+-- @+--   data World s = Entity+--     { position :: 'Component' s ''Field' (V2 Double)+--     , graphics :: 'Component' s ''Field' Graphics+--     , isPlayer :: 'Component' s ''Unique' ()+--     }+-- @+--+-- then @World 'SetterOf@ is equivalent to the following definition:+--+-- @+--   data World 'SetterOf = Entity+--     { position :: 'Update' (V2 Double)+--     , graphics :: 'Update' Graphics+--     , isPlayer :: 'Update' ()+--     }+-- @+--+-- 'unchanged' provides a @world 'SetterOf@ which will update no components,+-- and can have partial modifications added to it.+--+-- 'delEntity' provides a @world 'SetterOf@ which will delete all components+-- associated with the targeted entity.  ---------------------------------------------------------------------------------- | Provides a resumable 'SystemT'. This is a pretty big hack until I come up--- with a better formalization for everything.-yieldSystemT-    :: Monad m-    => SystemState world-    -> SystemT world m a-    -> m (SystemState world, a)-yieldSystemT = (fmap swap .) . flip S.runStateT ----------------------------------------------------------------------------------- | Evaluate a 'SystemT'.-runSystemT-    :: Monad m-    => world 'WorldOf-    -> SystemT world m a-    -> m a-runSystemT = flip evalStateT . (0,)------------------------------------------------------------------------------------ | Evaluate a 'System'.-runSystem-    :: world 'WorldOf-    -> System world a-    -> a-runSystem = (runIdentity .) . runSystemT------------------------------------------------------------------------------------ | Get the world.-getWorld-    :: Monad m-    => SystemT world m (world 'WorldOf)-getWorld = gets snd------------------------------------------------------------------------------------ | Only evaluate this 'QueryT' for entities which have the given component.-with-    :: Monad m-    => (world 'FieldOf -> Maybe a)-    -> QueryT world m ()-with = void . get------------------------------------------------------------------------------------ | Only evaluate this 'QueryT' for entities which do not have the given--- component.-without-    :: Monad m-    => (world 'FieldOf -> Maybe a)-    -> QueryT world m ()-without f = do-  e <- ask-  maybe (pure ()) (const mzero) $ f e------------------------------------------------------------------------------------ | Get the value of a component, failing the 'QueryT' if it isn't present.-get-    :: Monad m-    => (world 'FieldOf -> Maybe a)-    -> QueryT world m a-get f = do-  e <- ask-  maybe mzero pure $ f e-+-- $querying+-- #querying# The 'QueryT' monad provides functionality for performing+-- computations over an 'Ent''s components. The basic primitive is 'query',+-- which will pull the value of a component, and fail the query if it isn't+-- set.+--+-- For example, given the following world:+--+-- @+--   data World s = Entity+--     { position :: 'Component' s ''Field' (V2 Double)+--     , velocity :: 'Component' s ''Field' (V2 Double)+--     }+--     deriving ('Generic')+-- @+--+-- we could model a discrete time simulation via:+--+-- @+--   stepTime :: 'System' World ()+--   stepTime = do+--     'emap' 'allEnts' $ do+--       pos <- 'query' position+--       vel <- 'query' velocity+--       pure $ 'unchanged'+--         { position = 'Set' $ pos + vel+--         }+-- @+--+-- which will add an entity's velocity to its position, so long as it has both+-- components to begin with. ---------------------------------------------------------------------------------- | Attempt to get the value of a component.-getMaybe-    :: Monad m-    => (world 'FieldOf -> Maybe a)-    -> QueryT world m (Maybe a)-getMaybe f = fmap f ask 
− src/Data/Ecstasy/Deriving.hs
@@ -1,127 +0,0 @@-{-# LANGUAGE AllowAmbiguousTypes   #-}-{-# LANGUAGE DataKinds             #-}-{-# LANGUAGE FlexibleContexts      #-}-{-# LANGUAGE FlexibleInstances     #-}-{-# LANGUAGE KindSignatures        #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE ScopedTypeVariables   #-}-{-# LANGUAGE TypeApplications      #-}-{-# LANGUAGE TypeOperators         #-}--module Data.Ecstasy.Deriving where--import           Data.Ecstasy.Types (Update (..))-import           Data.IntMap.Strict (IntMap)-import qualified Data.IntMap.Strict as I-import           GHC.Generics---class GConvertSetter a b where-  gConvertSetter :: a x -> b x--instance GConvertSetter (K1 i a) (K1 i' (Maybe a)) where-  gConvertSetter (K1 a) = K1 $ Just a-  {-# INLINE gConvertSetter #-}--instance GConvertSetter (K1 i a) (K1 i' (Update a)) where-  gConvertSetter (K1 a) = K1 $ Set a-  {-# INLINE gConvertSetter #-}--instance GConvertSetter (K1 i (Maybe a)) (K1 i' (Update a)) where-  gConvertSetter (K1 (Just a)) = K1 $ Set a-  gConvertSetter (K1 Nothing)  = K1 Unset-  {-# INLINE gConvertSetter #-}--instance GConvertSetter f f' => GConvertSetter (M1 i c f) (M1 i' c' f') where-  gConvertSetter (M1 a) = M1 $ gConvertSetter a-  {-# INLINE gConvertSetter #-}--instance (GConvertSetter a c , GConvertSetter b d) => GConvertSetter (a :*: b) (c :*: d) where-  gConvertSetter (a :*: b) = gConvertSetter a :*: gConvertSetter b-  {-# INLINE gConvertSetter #-}---class GGetEntity a b where-  gGetEntity :: a x -> Int -> b x--instance GGetEntity (K1 i (IntMap a)) (K1 i' (Maybe a)) where-  gGetEntity (K1 a) e = K1 $ I.lookup e $ a-  {-# INLINE gGetEntity #-}--instance GGetEntity (K1 i (Maybe (Int, a))) (K1 i' (Maybe a)) where-  gGetEntity (K1 (Just (e', a))) e | e == e' = K1 $ Just a-  gGetEntity _ _ = K1 Nothing-  {-# INLINE gGetEntity #-}--instance GGetEntity f f' => GGetEntity (M1 i c f) (M1 i' c' f') where-  gGetEntity (M1 a) e = M1 $ gGetEntity a e-  {-# INLINE gGetEntity #-}--instance (GGetEntity a c , GGetEntity b d) => GGetEntity (a :*: b) (c :*: d) where-  gGetEntity (a :*: b) e = gGetEntity a e :*: gGetEntity b e-  {-# INLINE gGetEntity #-}---class GSetEntity a b where-  gSetEntity :: a x -> Int -> b x -> b x--instance GSetEntity (K1 i (Update a)) (K1 i' (Maybe (Int, a))) where-  gSetEntity (K1 (Set a)) e _ = K1 $ Just (e, a)-  gSetEntity (K1 Unset) e (K1 (Just (e', b))) =-    if e == e'-       then K1 Nothing-       else K1 $ Just (e', b)-  gSetEntity _  _ (K1 b) = K1 b-  {-# INLINE gSetEntity #-}--instance GSetEntity (K1 i (Update a)) (K1 i' (IntMap a)) where-  gSetEntity (K1 Keep) _ (K1 b) = K1 b-  gSetEntity (K1 (Set a)) e (K1 b) = K1 $ I.alter (const $ Just a) e b-  gSetEntity (K1 Unset) e (K1 b) = K1 $ I.alter (const Nothing) e b-  {-# INLINE gSetEntity #-}--instance GSetEntity f f' => GSetEntity (M1 i c f) (M1 i' c' f') where-  gSetEntity (M1 a) e (M1 b) = M1 $ gSetEntity a e b-  {-# INLINE gSetEntity #-}--instance (GSetEntity a c , GSetEntity b d) => GSetEntity (a :*: b) (c :*: d) where-  gSetEntity (a :*: b) e (c :*: d) = gSetEntity a e c :*: gSetEntity b e d-  {-# INLINE gSetEntity #-}---def :: forall keep a. (Generic a, GDefault keep (Rep a)) => a-def = to $ gdef @keep-{-# INLINE def #-}---class GDefault (keep :: Bool) f where-  gdef :: f a--instance GDefault keep U1 where-  gdef = U1-  {-# INLINE gdef #-}--instance GDefault keep (K1 i (Maybe c)) where-  gdef = K1 Nothing-  {-# INLINE gdef #-}--instance GDefault 'False (K1 i (Update c)) where-  gdef = K1 Unset-  {-# INLINE gdef #-}--instance GDefault 'True (K1 i (Update c)) where-  gdef = K1 Keep-  {-# INLINE gdef #-}--instance GDefault keep (K1 i (IntMap c)) where-  gdef = K1 I.empty-  {-# INLINE gdef #-}--instance GDefault keep f => GDefault keep (M1 i c f) where-  gdef = M1 $ gdef @keep-  {-# INLINE gdef #-}--instance (GDefault keep a, GDefault keep b) => GDefault keep (a :*: b) where-  gdef = gdef @keep :*: gdef @keep-  {-# INLINE gdef #-}-
+ src/Data/Ecstasy/Internal.hs view
@@ -0,0 +1,416 @@+{-# LANGUAGE AllowAmbiguousTypes    #-}+{-# LANGUAGE DataKinds              #-}+{-# LANGUAGE DefaultSignatures      #-}+{-# LANGUAGE FlexibleContexts       #-}+{-# LANGUAGE FlexibleInstances      #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE MonoLocalBinds         #-}+{-# LANGUAGE MultiParamTypeClasses  #-}+{-# LANGUAGE ScopedTypeVariables    #-}+{-# LANGUAGE TupleSections          #-}+{-# LANGUAGE TypeApplications       #-}+{-# LANGUAGE UndecidableInstances   #-}+{-# LANGUAGE ViewPatterns           #-}++module Data.Ecstasy.Internal where++import           Control.Arrow (first, second)+import           Control.Monad (mzero, void)+import           Control.Monad.Trans.Class (lift)+import           Control.Monad.Trans.Maybe (runMaybeT)+import           Control.Monad.Trans.Reader (runReaderT, asks)+import           Control.Monad.Trans.State.Strict (modify, get, gets, evalStateT)+import qualified Control.Monad.Trans.State.Strict as S+import           Data.Ecstasy.Internal.Deriving+import qualified Data.Ecstasy.Types as T+import           Data.Ecstasy.Types hiding (unEnt)+import           Data.Foldable (for_)+import           Data.Functor.Identity (Identity (..))+import           Data.Maybe (catMaybes)+import           Data.Traversable (for)+import           Data.Tuple (swap)+import           GHC.Generics+++------------------------------------------------------------------------------+-- | This class provides all of the functionality necessary to manipulate the+-- ECS.+class HasWorld' world => HasWorld world m where++  ----------------------------------------------------------------------------+  -- | Fetches an entity from the world given its 'Ent'.+  getEntity+      :: Monad m+      => Ent+      -> SystemT world m (world 'FieldOf)+  default getEntity+      :: ( Monad m+         , GGetEntity m+                      (Rep (world ('WorldOf m)))+                      (Rep (world 'FieldOf))+         , Generic (world 'FieldOf)+         , Generic (world ('WorldOf m))+         )+      => Ent+      -> SystemT world m (world 'FieldOf)+  getEntity e = do+    w <- SystemT $ gets snd+    lift . fmap to . gGetEntity @m (from w) $ T.unEnt e+  {-# INLINE getEntity #-}++  ----------------------------------------------------------------------------+  -- | Updates an 'Ent' in the world given its setter.+  setEntity+      :: Ent+      -> world 'SetterOf+      -> SystemT world m ()+  default setEntity+      :: ( GSetEntity m+                      (Rep (world 'SetterOf))+                      (Rep (world ('WorldOf m)))+         , Generic (world ('WorldOf m))+         , Generic (world 'SetterOf)+         , Monad m+         )+      => Ent+      -> world 'SetterOf+      -> SystemT world m ()+  setEntity e s = do+    w <- SystemT $ gets snd+    x <- lift . fmap to . gSetEntity (from s) (T.unEnt e) $ from w+    SystemT . modify . second $ const x+  {-# INLINE setEntity #-}++  ----------------------------------------------------------------------------+  -- | The default world, which contains only empty containers.+  defStorage :: world ('WorldOf m)+  default defStorage+      :: ( Generic (world ('WorldOf m))+         , GDefault 'True (Rep (world ('WorldOf m)))+         )+      => world ('WorldOf m)+  defStorage = def @'True+++class HasWorld' world where+  ----------------------------------------------------------------------------+  -- | Transforms an entity into a setter to transform the default entity into+  -- the given one. Used by 'createEntity'.+  convertSetter+      :: world 'FieldOf+      -> world 'SetterOf+  default convertSetter+      :: ( GConvertSetter (Rep (world 'FieldOf))+                          (Rep (world 'SetterOf))+         , Generic (world 'FieldOf)+         , Generic (world 'SetterOf)+         )+      => world 'FieldOf+      -> world 'SetterOf+  convertSetter = to . gConvertSetter . from+  {-# INLINE convertSetter #-}++  ----------------------------------------------------------------------------+  -- | The default entity, owning no components.+  newEntity :: world 'FieldOf+  default newEntity+      :: ( Generic (world 'FieldOf)+         , GDefault 'True (Rep (world 'FieldOf))+         )+      => world 'FieldOf+  newEntity = def @'True+  {-# INLINE newEntity #-}++  ----------------------------------------------------------------------------+  -- | The default setter, which keeps all components with their previous value.+  unchanged :: world 'SetterOf+  default unchanged+      :: ( Generic (world 'SetterOf)+         , GDefault 'True (Rep (world 'SetterOf))+         )+      => world 'SetterOf+  unchanged = def @'True+  {-# INLINE unchanged #-}++  ----------------------------------------------------------------------------+  -- | A setter which will delete the entity if its 'QueryT' matches.+  delEntity :: world 'SetterOf+  default delEntity+      :: ( Generic (world 'SetterOf)+         , GDefault 'False (Rep (world 'SetterOf))+         )+      => world 'SetterOf+  delEntity = def @'False+  {-# INLINE delEntity #-}+++instance ( Generic (world 'SetterOf)+         , Generic (world 'FieldOf)+         , GConvertSetter (Rep (world 'FieldOf))+                          (Rep (world 'SetterOf))+         , GDefault 'True  (Rep (world 'FieldOf))+         , GDefault 'False (Rep (world 'SetterOf))+         , GDefault 'True  (Rep (world 'SetterOf))+         ) => HasWorld' world+++instance ( HasWorld' world+         , Generic (world 'SetterOf)+         , Generic (world ('WorldOf m))+         , Generic (world 'FieldOf)+         , GConvertSetter (Rep (world 'FieldOf))+                          (Rep (world 'SetterOf))+         , GDefault 'True  (Rep (world 'FieldOf))+         , GDefault 'False (Rep (world 'SetterOf))+         , GDefault 'True  (Rep (world 'SetterOf))+         , GDefault 'True  (Rep (world ('WorldOf m)))+         , GSetEntity m+                      (Rep (world 'SetterOf))+                      (Rep (world ('WorldOf m)))+         , GGetEntity m+                      (Rep (world ('WorldOf m)))+                      (Rep (world 'FieldOf))+         , Monad m+         ) => HasWorld world m+++------------------------------------------------------------------------------+-- | Retrieve a unique 'Ent'.+nextEntity+    :: Monad m+    => SystemT a m Ent+nextEntity = do+  (e, _) <- SystemT S.get+  SystemT . modify . first . const $ e + 1+  pure $ Ent e+++------------------------------------------------------------------------------+-- | Create a new entity.+createEntity+    :: (HasWorld world m, Monad m)+    => world 'FieldOf+    -> SystemT world m Ent+createEntity cs = do+  e <- nextEntity+  setEntity e $ convertSetter cs+  pure e+++------------------------------------------------------------------------------+-- | Delete an entity.+deleteEntity+    :: (HasWorld world m, Monad m)+    => Ent+    -> SystemT world m ()+deleteEntity = flip setEntity delEntity+++------------------------------------------------------------------------------+-- | Evaluate a 'QueryT'.+unQueryT+  :: QueryT world m a+  -> Ent+  -> world 'FieldOf+  -> m (Maybe a)+unQueryT q e f = runMaybeT $ flip runReaderT (e, f) $ runQueryT' q+++------------------------------------------------------------------------------+-- | Map a 'QueryT' transformation over all entites that match it.+emap+    :: ( HasWorld world m+       , Monad m+       )+    => EntTarget world m+    -> QueryT world m (world 'SetterOf)+    -> SystemT world m ()+emap t f = do+  es <- t+  for_ es $ \e -> do+    cs <- getEntity e+    sets <- lift $ unQueryT f e cs+    for_ sets $ setEntity e+++------------------------------------------------------------------------------+-- | Collect the results of a monadic computation over every entity matching+-- a 'QueryT'.+efor+    :: ( HasWorld world m+       , Monad m+       )+    => EntTarget world m+    -> QueryT world m a+    -> SystemT world m [a]+efor t f = do+  es <- t+  fmap catMaybes $ for es $ \e -> do+    cs <- getEntity e+    lift $ unQueryT f e cs+++------------------------------------------------------------------------------+-- | Do an 'emap' and an 'efor' at the same time.+eover+    :: ( HasWorld world m+       , Monad m+       )+    => EntTarget world m+    -> QueryT world m (a, world 'SetterOf)+    -> SystemT world m [a]+eover t f = do+  es <- t+  fmap catMaybes $ for es $ \e -> do+    cs <- getEntity e+    mset <- lift $ unQueryT f e cs+    for mset $ \(a, setter) -> do+      setEntity e setter+      pure a+++------------------------------------------------------------------------------+-- | Run a 'QueryT' over a particular 'Ent'.+runQueryT+    :: ( HasWorld world m+       , Monad m+       )+    => Ent+    -> QueryT world m a+    -> SystemT world m (Maybe a)+runQueryT e qt = do+  cs <- getEntity e+  lift $ unQueryT qt e cs+++------------------------------------------------------------------------------+-- | Provides a resumable 'SystemT'. This is a pretty big hack until I come up+-- with a better formalization for everything.+yieldSystemT+    :: Monad m+    => SystemState world m+    -> SystemT world m a+    -> m (SystemState world m, a)+yieldSystemT w = fmap swap . flip S.runStateT w . runSystemT'+++------------------------------------------------------------------------------+-- | Evaluate a 'SystemT'.+runSystemT+    :: Monad m+    => world ('WorldOf m)+    -> SystemT world m a+    -> m a+runSystemT w = flip evalStateT (0, w) . runSystemT'+++------------------------------------------------------------------------------+-- | Evaluate a 'System'.+runSystem+    :: world ('WorldOf Identity)+    -> System world a+    -> a+runSystem = (runIdentity .) . runSystemT+++------------------------------------------------------------------------------+-- | Only evaluate this 'QueryT' for entities which have the given component.+with+    :: Monad m+    => (world 'FieldOf -> Maybe a)+    -> QueryT world m ()+with = void . query+{-# INLINE with #-}+++------------------------------------------------------------------------------+-- | Only evaluate this 'QueryT' for entities which do not have the given+-- component.+without+    :: Monad m+    => (world 'FieldOf -> Maybe a)+    -> QueryT world m ()+without f = do+  e <- QueryT $ asks snd+  maybe (pure ()) (const mzero) $ f e+++------------------------------------------------------------------------------+-- | Get the value of a component, failing the 'QueryT' if it isn't present.+query+    :: Monad m+    => (world 'FieldOf -> Maybe a)+    -> QueryT world m a+query f = do+  e <- QueryT $ asks snd+  maybe mzero pure $ f e+{-# INLINE query #-}+++------------------------------------------------------------------------------+-- | Attempt to get the value of a component.+queryMaybe+    :: Monad m+    => (world 'FieldOf -> Maybe a)+    -> QueryT world m (Maybe a)+queryMaybe f = fmap f $ QueryT $ asks snd+++------------------------------------------------------------------------------+-- | Get the 'Ent' for whom this query is running.+queryEnt+    :: Monad m+    => QueryT world m Ent+queryEnt = QueryT $ asks fst+++------------------------------------------------------------------------------+-- | Query a flag as a 'Bool'.+queryFlag+    :: Monad m+    => (world 'FieldOf -> Maybe ())+    -> QueryT world m Bool+queryFlag = fmap (maybe False (const True)) . queryMaybe+++------------------------------------------------------------------------------+-- | Perform a query with a default.+queryDef+    :: Monad m+    => z+    -> (world 'FieldOf -> Maybe z)+    -> QueryT world m z+queryDef z = fmap (maybe z id) . queryMaybe+++------------------------------------------------------------------------------+-- | An 'EntTarget' is a set of 'Ent's to iterate over.+type EntTarget world m = SystemT world m [Ent]+++------------------------------------------------------------------------------+-- | Iterate over all entities.+allEnts :: Monad m => EntTarget world m+allEnts = do+  (es, _) <- SystemT get+  pure $ Ent <$> [0 .. es - 1]+++------------------------------------------------------------------------------+-- | Iterate over some entities.+someEnts :: Monad m => [Ent] -> EntTarget world m+someEnts = pure+++------------------------------------------------------------------------------+-- | Iterate over an entity.+anEnt :: Monad m => Ent -> EntTarget world m+anEnt = pure . pure+++------------------------------------------------------------------------------+-- | Turn a 'Maybe' into an 'Update'.+maybeToUpdate :: Maybe a -> Update a+maybeToUpdate Nothing  = Unset+maybeToUpdate (Just a) = Set a+
+ src/Data/Ecstasy/Internal/Deriving.hs view
@@ -0,0 +1,154 @@+{-# LANGUAGE AllowAmbiguousTypes       #-}+{-# LANGUAGE DataKinds                 #-}+{-# LANGUAGE FlexibleContexts          #-}+{-# LANGUAGE FlexibleInstances         #-}+{-# LANGUAGE KindSignatures            #-}+{-# LANGUAGE MultiParamTypeClasses     #-}+{-# LANGUAGE NoMonomorphismRestriction #-}+{-# LANGUAGE ScopedTypeVariables       #-}+{-# LANGUAGE TypeApplications          #-}+{-# LANGUAGE TypeOperators             #-}+{-# LANGUAGE UndecidableInstances      #-}++module Data.Ecstasy.Internal.Deriving where++import           Data.Ecstasy.Types (Update (..), VTable (..), Ent (..))+import           Data.IntMap (IntMap)+import qualified Data.IntMap as I+import           Data.Proxy (Proxy (..))+import           GHC.Generics+import           GHC.TypeLits+++class GConvertSetter a b where+  gConvertSetter :: a x -> b x++instance GConvertSetter (K1 i a) (K1 i' (Maybe a)) where+  gConvertSetter (K1 a) = K1 $ Just a+  {-# INLINE gConvertSetter #-}++instance GConvertSetter (K1 i a) (K1 i' (Update a)) where+  gConvertSetter (K1 a) = K1 $ Set a+  {-# INLINE gConvertSetter #-}++instance GConvertSetter (K1 i (Maybe a)) (K1 i' (Update a)) where+  gConvertSetter (K1 (Just a)) = K1 $ Set a+  gConvertSetter (K1 Nothing)  = K1 Unset+  {-# INLINE gConvertSetter #-}++instance GConvertSetter f f' => GConvertSetter (M1 i c f) (M1 i' c' f') where+  gConvertSetter (M1 a) = M1 $ gConvertSetter a+  {-# INLINE gConvertSetter #-}++instance (GConvertSetter a c , GConvertSetter b d) => GConvertSetter (a :*: b) (c :*: d) where+  gConvertSetter (a :*: b) = gConvertSetter a :*: gConvertSetter b+  {-# INLINE gConvertSetter #-}+++class GGetEntity m a b where+  gGetEntity :: a x -> Int -> m (b x)++instance (Applicative m)+      => GGetEntity m (K1 i (VTable m a)) (K1 i' (Maybe a)) where+  gGetEntity (K1 (VTable vget _)) e = fmap K1 $ vget $ Ent e+  {-# INLINE gGetEntity #-}++instance Applicative m => GGetEntity m (K1 i (IntMap a)) (K1 i' (Maybe a)) where+  gGetEntity (K1 a) e = pure . K1 $ I.lookup e $ a+  {-# INLINE gGetEntity #-}++instance Applicative m => GGetEntity m (K1 i (Maybe (Int, a))) (K1 i' (Maybe a)) where+  gGetEntity (K1 (Just (e', a))) e | e == e' = pure . K1 $ Just a+  gGetEntity _ _ = pure $ K1 Nothing+  {-# INLINE gGetEntity #-}++instance (Functor m, GGetEntity m f f') => GGetEntity m (M1 i c f) (M1 i' c' f') where+  gGetEntity (M1 a) e = fmap M1 $ gGetEntity a e+  {-# INLINE gGetEntity #-}++instance (Applicative m, GGetEntity m a c , GGetEntity m b d) => GGetEntity m (a :*: b) (c :*: d) where+  gGetEntity (a :*: b) e = (:*:) <$> gGetEntity a e <*> gGetEntity b e+  {-# INLINE gGetEntity #-}+++class GSetEntity m a b where+  gSetEntity :: a x -> Int -> b x -> m (b x)++instance Applicative m => GSetEntity m (K1 i (Update a)) (K1 i' (Maybe (Int, a))) where+  gSetEntity (K1 (Set a)) e _ = pure . K1 $ Just (e, a)+  gSetEntity (K1 Unset) e (K1 (Just (e', b))) =+    pure $ if e == e'+       then K1 Nothing+       else K1 $ Just (e', b)+  gSetEntity _  _ (K1 b) = pure $ K1 b+  {-# INLINE gSetEntity #-}++instance (Applicative m)+      => GSetEntity m (K1 i (Update a)) (K1 i' (VTable m a)) where+  gSetEntity (K1 a) e (K1 z@(VTable _ vset)) =+    vset (Ent e) a *> pure (K1 z)+  {-# INLINE gSetEntity #-}++instance Applicative m => GSetEntity m (K1 i (Update a)) (K1 i' (IntMap a)) where+  gSetEntity (K1 Keep) _ (K1 b) = pure $ K1 b+  gSetEntity (K1 (Set a)) e (K1 b) = pure . K1 $ I.alter (const $ Just a) e b+  gSetEntity (K1 Unset) e (K1 b) = pure . K1 $ I.alter (const Nothing) e b+  {-# INLINE gSetEntity #-}++instance (Functor m, GSetEntity m f f') => GSetEntity m (M1 i c f) (M1 i' c' f') where+  gSetEntity (M1 a) e (M1 b) = fmap M1 $ gSetEntity a e b+  {-# INLINE gSetEntity #-}++instance (Applicative m, GSetEntity m a c, GSetEntity m b d) => GSetEntity m (a :*: b) (c :*: d) where+  gSetEntity (a :*: b) e (c :*: d) = (:*:) <$> gSetEntity a e c <*> gSetEntity b e d+  {-# INLINE gSetEntity #-}+++def :: forall keep a. (Generic a, GDefault keep (Rep a)) => a+def = to $ gdef @keep+{-# INLINE def #-}+++class GDefault (keep :: Bool) f where+  gdef :: f a++instance GDefault keep U1 where+  gdef = U1+  {-# INLINE gdef #-}++instance GDefault keep (K1 i (Maybe c)) where+  gdef = K1 Nothing+  {-# INLINE gdef #-}++instance GDefault 'False (K1 i (Update c)) where+  gdef = K1 Unset+  {-# INLINE gdef #-}++instance GDefault 'True (K1 i (Update c)) where+  gdef = K1 Keep+  {-# INLINE gdef #-}++instance GDefault keep (K1 i (IntMap c)) where+  gdef = K1 I.empty+  {-# INLINE gdef #-}++instance {-# OVERLAPPING #-} (Applicative m, KnownSymbol sym)+      => GDefault keep (M1 S ('MetaSel ('Just sym) x y z) (K1 i (VTable m a))) where+  gdef = M1 $ K1 $ VTable (const err) (const $ const err)+    where+      err :: err+      err = error $ mconcat+            [ "unset VTable for Virtual component '"+            , symbolVal $ Proxy @sym+            , "'"+            ]+  {-# INLINE gdef #-}++instance GDefault keep f => GDefault keep (M1 i c f) where+  gdef = M1 $ gdef @keep+  {-# INLINE gdef #-}++instance (GDefault keep a, GDefault keep b) => GDefault keep (a :*: b) where+  gdef = gdef @keep :*: gdef @keep+  {-# INLINE gdef #-}+
src/Data/Ecstasy/Types.hs view
@@ -1,14 +1,32 @@-{-# LANGUAGE DataKinds      #-}-{-# LANGUAGE KindSignatures #-}-{-# LANGUAGE TypeFamilies   #-}+{-# LANGUAGE DataKinds                  #-}+{-# LANGUAGE DeriveFoldable             #-}+{-# LANGUAGE DeriveFunctor              #-}+{-# LANGUAGE DeriveTraversable          #-}+{-# LANGUAGE FlexibleInstances          #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE KindSignatures             #-}+{-# LANGUAGE MultiParamTypeClasses      #-}+{-# LANGUAGE ScopedTypeVariables        #-}+{-# LANGUAGE TypeFamilies               #-}+{-# LANGUAGE TypeInType                 #-}+{-# LANGUAGE UndecidableInstances       #-}+{-# OPTIONS_GHC -funbox-strict-fields   #-}  module Data.Ecstasy.Types where -import Control.Monad.Trans.Maybe (MaybeT)-import Control.Monad.Trans.Reader (ReaderT)-import Control.Monad.Trans.State.Strict (StateT)+import Control.Applicative (Alternative)+import Control.Monad (MonadPlus)+import Control.Monad.IO.Class (MonadIO)+import Control.Monad.Reader.Class (MonadReader (..))+import Control.Monad.State.Class (MonadState (..))+import Control.Monad.Trans.Class (MonadTrans (..))+import Control.Monad.Trans.Maybe (MaybeT (..))+import Control.Monad.Trans.Reader (ReaderT (..))+import Control.Monad.Trans.State.Strict (StateT (..))+import Control.Monad.Writer.Class (MonadWriter) import Data.Functor.Identity (Identity) import Data.IntMap.Strict (IntMap)+import Data.Kind   ------------------------------------------------------------------------------@@ -22,36 +40,83 @@  ------------------------------------------------------------------------------ -- | The internal state of the 'SystemT' monad.-type SystemState w = (Int, w 'WorldOf)+type SystemState w m = (Int, w ('WorldOf m))  ------------------------------------------------------------------------------ -- | A monad transformer over an ECS given a world 'w'.-type SystemT w = StateT (SystemState w)+newtype SystemT w m a = SystemT+  { runSystemT' :: StateT (SystemState w m) m a+  }+  deriving ( Functor+           , Applicative+           , Monad+           , MonadReader r+           , MonadWriter ww+           , MonadIO+           ) +instance MonadTrans (SystemT w) where+  lift = SystemT . lift++instance MonadState s m => MonadState s (SystemT w m) where+  get = SystemT . lift $ get+  put = SystemT . lift . put++ ------------------------------------------------------------------------------ -- | A monad over an ECS given a world 'w'.-type System  w = SystemT w Identity+type System w = SystemT w Identity + ------------------------------------------------------------------------------ -- | A computation to run over a particular entity.-type QueryT w m = ReaderT (w 'FieldOf) (MaybeT m)+newtype QueryT w m a = QueryT+  { runQueryT' :: ReaderT (Ent, w 'FieldOf) (MaybeT m) a+  }+  deriving ( Functor+           , Applicative+           , Monad+           , MonadState s+           , MonadWriter ww+           , MonadIO+           , Alternative+           , MonadPlus+           ) +instance MonadTrans (QueryT w) where+  lift = QueryT . lift . lift +instance MonadReader r m => MonadReader r (QueryT w m) where+  ask = QueryT $ lift ask+  local f = QueryT . runQueryT' . local f++ ------------------------------------------------------------------------------+-- | A collection of methods necessary to dispatch reads and writes to+-- a 'Virtual' component.+data VTable m a = VTable+  { -- | Get the value of an entity's component.+    vget :: !(Ent -> m (Maybe a))++    -- | Update the value of an entity's component.+  , vset :: !(Ent -> Update a -> m ())+  }+++------------------------------------------------------------------------------ -- | Data kind used to parameterize the ECS record. data StorageType-  = FieldOf   -- ^ Used to construct the actual entity.-  | WorldOf   -- ^ Used to construct the world's storage.+  = FieldOf   -- ^ Used to describe the actual entity.+  | WorldOf (Type -> Type)  -- ^ Used to construct the world's storage.   | SetterOf  -- ^ Used to construct a setter to update an entity.-  deriving (Eq, Ord, Show, Read, Enum, Bounded)   ------------------------------------------------------------------------------ -- | Data kind used to parameterize the fields of the ECS record. data ComponentType-  = Field   -- ^ This component can be owned by any entity.-  | Unique  -- ^ This component can be owned by only a single entity at a time.-  deriving (Eq, Ord, Show, Read, Enum, Bounded)+  = Field      -- ^ This component can be owned by any entity.+  | Unique     -- ^ This component can be owned by only a single entity at a time.+  | Virtual    -- ^ This component is owned by another system.   ------------------------------------------------------------------------------@@ -59,18 +124,19 @@ data Update a   = Keep   -- ^ Keep the current value.   | Unset  -- ^ Delete the current value if it exists.-  | Set a  -- ^ Set the current value.-  deriving (Eq, Ord, Show, Read)+  | Set !a  -- ^ Set the current value.+  deriving (Eq, Ord, Show, Read, Functor, Foldable, Traversable)   ------------------------------------------------------------------------------ -- | A type family to be used in your ECS recrod. type family Component (s :: StorageType)                       (c :: ComponentType)-                      (a :: *) :: * where+                      (a :: Type) :: Type where   Component 'FieldOf  c      a = Maybe a   Component 'SetterOf c      a = Update a -  Component 'WorldOf 'Field  a = IntMap a-  Component 'WorldOf 'Unique a = Maybe (Int, a)+  Component ('WorldOf m) 'Field   a = IntMap a+  Component ('WorldOf m) 'Unique  a = Maybe (Int, a)+  Component ('WorldOf m) 'Virtual a = VTable m a