A SystemT is a newtype around `ReaderT w m a`, where w is the game world variable.
Systems serve to
Allow type-based lookup of a component's store through
getStore.Lift side effects into their host Monad.
:: a typeCtrl KGHC 10.0.0.20260917 · lts/ghc-10.0.x · fc4e598 · 2026-10-11
Moduleapecs-0.10.1Haskell2010
This module forms the apecs Prelude. It selectively re-exports the user-facing functions from the submodules.
A SystemT is a newtype around `ReaderT w m a`, where w is the game world variable.
Systems serve to
Allow type-based lookup of a component's store through getStore.
Lift side effects into their host Monad.
A component is defined by specifying how it is stored. The constraint ensures that stores and components are mapped one-to-one.
type family Storage cComponent EntityDefined in apecs-0.10.1 · Apecs.Components · orphanComponent EntityCounterDefined in apecs-0.10.1 · Apecs.UtilComponent ()Defined in apecs-0.10.1 · Apecs.Components · orphanComponent c => Component (Filter c)Defined in apecs-0.10.1 · Apecs.ComponentsComponent c => Component (Not c)Defined in apecs-0.10.1 · Apecs.ComponentsComponent c => Component (Child c)Defined in apecs-0.10.1 · Apecs.Experimental.ChildrenComponent c => Component (ChildList c)Defined in apecs-0.10.1 · Apecs.Experimental.ChildrenComponent c => Component (ChildValue c)Defined in apecs-0.10.1 · Apecs.Experimental.ChildrenComponent c => Component (Head c)Defined in apecs-0.10.1 · Apecs.Experimental.ComponentsComponent c => Component (Redirect c)Defined in apecs-0.10.1 · Apecs.Experimental.ComponentsComponent c => Component (Identity c)Defined in apecs-0.10.1 · Apecs.Components · orphanIdentity component. Identity c is equivalent to c, so mostly useless.
Component c => Component (Maybe c)Defined in apecs-0.10.1 · Apecs.Components · orphan(Storage c ~ Pushdown s c, Component c) => Component (Stack c)Defined in apecs-0.10.1 · Apecs.Experimental.Stores(Component ca, Component cb) => Component (Either ca cb)Defined in apecs-0.10.1 · Apecs.Components · orphan(Component t_0, Component t_1) => Component (t_0, t_1)Defined in apecs-0.10.1 · Apecs.Components · orphan(Component t_0, Component t_1, Component t_2) => Component (t_0, t_1, t_2)Defined in apecs-0.10.1 · Apecs.Components · orphan(Component t_0, Component t_1, Component t_2, Component t_3) => Component (t_0, t_1, t_2, t_3)Defined in apecs-0.10.1 · Apecs.Components · orphan(Component t_0, Component t_1, Component t_2, Component t_3, Component t_4) => Component (t_0, t_1, t_2, t_3, t_4)Defined in apecs-0.10.1 · Apecs.Components · orphan(Component t_0, Component t_1, Component t_2, Component t_3, Component t_4, Component t_5) => Component (t_0, t_1, t_2, t_3, t_4, t_5)Defined in apecs-0.10.1 · Apecs.Components · orphan(Component t_0, Component t_1, Component t_2, Component t_3, Component t_4, Component t_5, Component t_6) => Component (t_0, t_1, t_2, t_3, t_4, t_5, t_6)Defined in apecs-0.10.1 · Apecs.Components · orphan(Component t_0, Component t_1, Component t_2, Component t_3, Component t_4, Component t_5, Component t_6, Component t_7) => Component (t_0, t_1, t_2, t_3, t_4, t_5, t_6, t_7)Defined in apecs-0.10.1 · Apecs.Components · orphanAn Entity is just an integer, used to index into a component store.
In general, use newEntity, cmap, and component tags instead of manipulating these directly.
For performance reasons, negative values like (-1) are reserved for stores to represent special values, so avoid using these.
Enum EntityDefined in apecs-0.10.1 · Apecs.CoreEq EntityDefined in apecs-0.10.1 · Apecs.CoreNum EntityDefined in apecs-0.10.1 · Apecs.CoreOrd EntityDefined in apecs-0.10.1 · Apecs.CoreShow EntityDefined in apecs-0.10.1 · Apecs.CoreComponent EntityDefined in apecs-0.10.1 · Apecs.Components · orphanMonad m => Has w m EntityDefined in apecs-0.10.1 · Apecs.Components · orphantype Storage Entity = EntityStoreDefined in apecs-0.10.1 · Apecs.Components · orphanHas w m c means that world w can produce a Storage c.
It is parameterized over m to allow stores to be foreign.
Monad m => Has w m EntityDefined in apecs-0.10.1 · Apecs.Components · orphanMonad m => Has w m ()Defined in apecs-0.10.1 · Apecs.Components · orphanHas w m c => Has w m (Filter c)Defined in apecs-0.10.1 · Apecs.ComponentsHas w m c => Has w m (Not c)Defined in apecs-0.10.1 · Apecs.ComponentsHas w m c => Has w m (Head c)Defined in apecs-0.10.1 · Apecs.Experimental.ComponentsHas w m c => Has w m (Redirect c)Defined in apecs-0.10.1 · Apecs.Experimental.ComponentsHas w m c => Has w m (Identity c)Defined in apecs-0.10.1 · Apecs.Components · orphanHas w m c => Has w m (Maybe c)Defined in apecs-0.10.1 · Apecs.Components · orphan(Storage c ~ Pushdown s c, Has w m c) => Has w m (Stack c)Defined in apecs-0.10.1 · Apecs.Experimental.Stores(MonadIO m, Component c, Has w m (Child c)) => Has w m (ChildList c)Defined in apecs-0.10.1 · Apecs.Experimental.Children(MonadIO m, Component c, Has w m (Child c)) => Has w m (ChildValue c)Defined in apecs-0.10.1 · Apecs.Experimental.Children(Has w m ca, Has w m cb) => Has w m (Either ca cb)Defined in apecs-0.10.1 · Apecs.Components · orphan(Has w m t_0, Has w m t_1) => Has w m (t_0, t_1)Defined in apecs-0.10.1 · Apecs.Components · orphan(Has w m t_0, Has w m t_1, Has w m t_2) => Has w m (t_0, t_1, t_2)Defined in apecs-0.10.1 · Apecs.Components · orphan(Has w m t_0, Has w m t_1, Has w m t_2, Has w m t_3) => Has w m (t_0, t_1, t_2, t_3)Defined in apecs-0.10.1 · Apecs.Components · orphan(Has w m t_0, Has w m t_1, Has w m t_2, Has w m t_3, Has w m t_4) => Has w m (t_0, t_1, t_2, t_3, t_4)Defined in apecs-0.10.1 · Apecs.Components · orphan(Has w m t_0, Has w m t_1, Has w m t_2, Has w m t_3, Has w m t_4, Has w m t_5) => Has w m (t_0, t_1, t_2, t_3, t_4, t_5)Defined in apecs-0.10.1 · Apecs.Components · orphan(Has w m t_0, Has w m t_1, Has w m t_2, Has w m t_3, Has w m t_4, Has w m t_5, Has w m t_6) => Has w m (t_0, t_1, t_2, t_3, t_4, t_5, t_6)Defined in apecs-0.10.1 · Apecs.Components · orphan(Has w m t_0, Has w m t_1, Has w m t_2, Has w m t_3, Has w m t_4, Has w m t_5, Has w m t_6, Has w m t_7) => Has w m (t_0, t_1, t_2, t_3, t_4, t_5, t_6, t_7)Defined in apecs-0.10.1 · Apecs.Components · orphanPseudocomponent indicating the absence of a.
Mainly used as e.g. cmap $ (a, Not b) -> c to iterate over entities with an a but no b.
Can also be used to delete components, like cmap $ a -> (Not :: Not a) to delete every a component.
A map based on Data.IntMap.Strict. O(log(n)) for most operations.
MonadIO m => ExplInit m (Map c)Defined in apecs-0.10.1 · Apecs.Stores.InternalMonadIO m => ExplDestroy m (Map c)Defined in apecs-0.10.1 · Apecs.Stores.Internal(MonadIO m, Typeable c) => ExplGet m (Map c)Defined in apecs-0.10.1 · Apecs.Stores.InternalMonadIO m => ExplMembers m (Map c)Defined in apecs-0.10.1 · Apecs.Stores.InternalMonadIO m => ExplSet m (Map c)Defined in apecs-0.10.1 · Apecs.Stores.InternalCachable (Map s)Defined in apecs-0.10.1 · Apecs.Stores.Internaltype Elem (Map c) = cDefined in apecs-0.10.1 · Apecs.Stores.InternalA Unique contains zero or one component. Writing to it overwrites both the previous component and its owner. Its main purpose is to be a Map optimized for when only ever one component inhabits it.
MonadIO m => ExplInit m (Unique c)Defined in apecs-0.10.1 · Apecs.Stores.InternalMonadIO m => ExplDestroy m (Unique c)Defined in apecs-0.10.1 · Apecs.Stores.Internal(MonadIO m, Typeable c) => ExplGet m (Unique c)Defined in apecs-0.10.1 · Apecs.Stores.InternalMonadIO m => ExplMembers m (Unique c)Defined in apecs-0.10.1 · Apecs.Stores.InternalMonadIO m => ExplSet m (Unique c)Defined in apecs-0.10.1 · Apecs.Stores.Internaltype Elem (Unique c) = cDefined in apecs-0.10.1 · Apecs.Stores.InternalA Global contains exactly one component. The initial value is mempty from the component's Monoid instance. Querying a Global at any Entity yields this one component, effectively sharing the component between all entities.
A Global component can be read with or get 0 or even get 1.
The convenience entity get undefinedglobal is defined as -1, and can be used to make operations on a global more explicit, i.e. 'Time t <- get global'.
You also can read and write Globals during a cmap over other components.
(Monoid c, MonadIO m) => ExplInit m (Global c)Defined in apecs-0.10.1 · Apecs.Stores.InternalMonadIO m => ExplGet m (Global c)Defined in apecs-0.10.1 · Apecs.Stores.InternalMonadIO m => ExplSet m (Global c)Defined in apecs-0.10.1 · Apecs.Stores.Internaltype Elem (Global c) = cDefined in apecs-0.10.1 · Apecs.Stores.InternalA cache for arbitrary types, using a boxed vector, adding an extra indirection for each component.
A cache for storable types, using a storable vector, storing components directly in the cache.
A cache for unboxed types, using an unboxed vector, storing components directly in the cache.
A cache around another store. Caches store their members in a fixed-size vector, so read/write operations become O(1). Caches can provide huge performance boosts, especially when working with large numbers of components.
The cache size is given as a type-level argument.
Note that iterating over a cache is linear in cache size, so sparsely populated caches might decrease performance. In general, the exact size of the cache does not matter as long as it reasonably approximates the number of components present.
The cache uses entity (-2) internally to represent missing entities. If you manually manipulate Entity values, be careful that you do not use (-2)
The actual cache is not necessarily the given argument, but the next biggest power of two. This is allows most operations to be expressed as bit masks, for a large potential performance boost.
(MonadIO m, ExplInit m s, KnownNat n, Cachable s, MVector v (Elem s)) => ExplInit m (GCache v n s)Defined in apecs-0.10.1 · Apecs.Stores.Internal(MonadIO m, ExplDestroy m s, MVector v (Elem s), Elem s ~ Elem (GCache v n s)) => ExplDestroy m (GCache v n s)Defined in apecs-0.10.1 · Apecs.Stores.Internal(MonadIO m, ExplGet m s, MVector v (Elem s), Elem s ~ Elem (GCache v n s)) => ExplGet m (GCache v n s)Defined in apecs-0.10.1 · Apecs.Stores.Internal(MonadIO m, ExplMembers m s) => ExplMembers m (GCache v n s)Defined in apecs-0.10.1 · Apecs.Stores.Internal(MonadIO m, ExplSet m s, MVector v (Elem s), Elem s ~ Elem (GCache v n s)) => ExplSet m (GCache v n s)Defined in apecs-0.10.1 · Apecs.Stores.Internal(KnownNat n, Cachable s) => Cachable (Cache n s)Defined in apecs-0.10.1 · Apecs.Stores.Internaltype Elem (GCache v n s) = Elem sDefined in apecs-0.10.1 · Apecs.Stores.InternalInitialize a new empty store.
Read a Component
Writes a Component to a given Entity. Will overwrite existing Components.
Writes a Component to a given Entity. Will overwrite existing Components.
set operator
Destroys component c for the given entity.
Returns whether the given entity has component c
Applies a function, if possible.
Applies a function, if possible.
modify operator
Maps a function over all entities with a cx, and writes their cy.
Conditional cmap, that first tests whether the argument satisfies some property.
The entity needs to have both a cx and cp component.
Monadically iterates over all entites with a cx, and writes their cy.
Monadically iterates over all entites with a cx
Fold over the game world; for example, cfold max (minBound :: Foo) will find the maximum value of Foo.
Strict in the accumulator.
Monadically fold over the game world. Strict in the accumulator.
Monadically fold over the game world. Strict in the accumulator.
Collect matching components into a list by using the specified test/process function.
You can use this to preprocess data before returning.
And you can do a test here that depends on data from multiple components.
Pass Just to simply collect all the items.
When using cmap or cfold over a tuple of components, keep in mind the ordering of the tuple can have performance implications!
For tuples, the way the cmap and cfold work under the hood is by iterating over the component in the first position, and then for each entity that has that component, checking whether the entity also has the components in the remaining positions. Therefore, the first component will typically be the most determining factor for performance, and a good rule of thumb is to, when iterating over a tuple, put the rarest component in first position.
Let's take a look at an example. Consider a simple 2D rendering system built on top of cmapM_:
cmapM_ $ \(Sprite sprite, Visible) -> do
renderSprite sprite
While this rendering system works, it could be made more efficient by
leveraging knowledge of how the library handles reading of tupled components.
The usage of cmapM_ here (or any of the other map/fold functions) will
iterate over all entities with a Sprite component and filter out any of
these entities that do not have a Visible component. Depending on the game,
it is reasonable to assume that there are more sprites active in the game's
world than sprites that are visible to the game's camera.
Swapping the component ordering in the tuple is likely to be more efficient:
cmapM_ $ \(Visible, Sprite sprite) -> do
renderSprite sprite
Now the system iterates over just those entities that are visible to the
game's camera and filters out any that do not have a Sprite component.
While putting the rarest component first is an excellent rule of thumb, to get the best possible performance, always consider how maps and folds are executed under the hood, and how you can order your components to optimize that process.
Run a system in a game world
Run a system in a game world
Explicitly invoke the garbage collector
Component used by newEntity to track the number of issued entities.
Automatically added to any world created with makeWorld
Eq EntityCounterDefined in apecs-0.10.1 · Apecs.UtilShow EntityCounterDefined in apecs-0.10.1 · Apecs.UtilSemigroup EntityCounterDefined in apecs-0.10.1 · Apecs.UtilMonoid EntityCounterDefined in apecs-0.10.1 · Apecs.UtilComponent EntityCounterDefined in apecs-0.10.1 · Apecs.Utiltype Storage EntityCounter = ReadOnly (Global EntityCounter)Defined in apecs-0.10.1 · Apecs.UtilWrites the given components to a new entity, and yields that entity. The return value is often ignored.
Writes the given components to a new entity without yelding the result. Used mostly for convenience.
Convenience entity, for use in places where the entity value does not matter, i.e. a global store.
The typical way to create a world record, associated Has instances, and initialization function.
makeWorld "MyWorld" [''Component1, ''Component2, ...]turns into
data MyWorld = MyWorld Component1 Component2 ... EntityCounter
instance MyWorld `Has` Component1 where ...
instance MyWorld `Has` Component2 where ...
...
instance MyWorld `Has` EntityCounter where ...
initMyWorld :: IO MyWorld
initMyWorld = MyWorld <$> initStore <*> initStore <*> ... <*> initStoreCalls makeWorld and makeMapComponents, i.e. makes a world and also defines Component instances with a Map store.
Fetch the value of the environment.
Lift a computation from the IO monad. This allows us to run IO computations in any monadic stack, so long as it supports these kinds of operations (i.e. IO is the base monad for the stack).
import Control.Monad.Trans.State -- from the "transformers" library
printState :: Show s => StateT s IO ()
printState = do
state <- get
liftIO $ print stateHad we omitted liftIO, we would have ended up with this error:
• Couldn't match type ‘IO’ with ‘StateT s IO’
Expected type: StateT s IO ()
Actual type: IO ()The important part here is the mismatch between StateT s IO () and IO ().
Luckily, we know of a function that takes an IO a and returns an (m a): liftIO,
enabling us to run the program and see the expected results:
> evalStateT printState "hello"
"hello"
> evalStateT printState 3
3
Lift a computation from the argument monad to the constructed monad.
Proxy is a type that holds no data, but has a phantom parameter of arbitrary type (or even kind). Its use is to provide type information, even though there is no value available of that type (or it may be too costly to create one).
Historically, Proxy :: Proxy a is a safer alternative to the
idiom.undefined :: a
Proxy :: Proxy (Void, Int -> Int)Proxy
Proxy can even hold types of higher kinds,
Proxy :: Proxy EitherProxy
Proxy :: Proxy FunctorProxy
Proxy :: Proxy complicatedStructureProxy
Generic1 ProxyDefined in ghc-internal-10.0.0 · GHC.Internal.GenericsMonad ProxyDefined in ghc-internal-10.0.0 · GHC.Internal.Data.ProxyFunctor ProxyDefined in ghc-internal-10.0.0 · GHC.Internal.Data.ProxyApplicative ProxyDefined in ghc-internal-10.0.0 · GHC.Internal.Data.ProxyFoldable ProxyDefined in ghc-internal-10.0.0 · GHC.Internal.Data.FoldableTraversable ProxyDefined in ghc-internal-10.0.0 · GHC.Internal.Data.TraversableAlternative ProxyDefined in ghc-internal-10.0.0 · GHC.Internal.Data.ProxyMonadPlus ProxyDefined in ghc-internal-10.0.0 · GHC.Internal.Data.ProxyMonadZip ProxyDefined in ghc-internal-10.0.0 · GHC.Internal.Control.Monad.ZipEq1 ProxyDefined in base-4.23.0.0 · Data.Functor.ClassesOrd1 ProxyDefined in base-4.23.0.0 · Data.Functor.ClassesRead1 ProxyDefined in base-4.23.0.0 · Data.Functor.ClassesShow1 ProxyDefined in base-4.23.0.0 · Data.Functor.ClassesContravariant ProxyDefined in base-4.23.0.0 · Data.Functor.ContravariantNFData1 ProxyDefined in deepseq-1.5.2.0 · Control.DeepSeqBounded (Proxy t)Defined in ghc-internal-10.0.0 · GHC.Internal.Data.ProxyEnum (Proxy s)Defined in ghc-internal-10.0.0 · GHC.Internal.Data.ProxyEq (Proxy s)Defined in ghc-internal-10.0.0 · GHC.Internal.Data.ProxyData t => Data (Proxy t)Defined in ghc-internal-10.0.0 · GHC.Internal.Data.DataOrd (Proxy s)Defined in ghc-internal-10.0.0 · GHC.Internal.Data.ProxyRead (Proxy t)Defined in ghc-internal-10.0.0 · GHC.Internal.Data.ProxyShow (Proxy s)Defined in ghc-internal-10.0.0 · GHC.Internal.Data.ProxyIx (Proxy s)Defined in ghc-internal-10.0.0 · GHC.Internal.Data.ProxyGeneric (Proxy t)Defined in ghc-internal-10.0.0 · GHC.Internal.GenericsSemigroup (Proxy s)Defined in ghc-internal-10.0.0 · GHC.Internal.Data.ProxyMonoid (Proxy s)Defined in ghc-internal-10.0.0 · GHC.Internal.Data.ProxyNFData (Proxy a)Defined in deepseq-1.5.2.0 · Control.DeepSeqtype Rep (Proxy t) = D1 ('MetaData "Proxy"
"GHC.Internal.Data.Proxy"
"ghc-internal"
'False) (C1 ('MetaCons "Proxy"
'PrefixI 'False) U1)Defined in ghc-internal-10.0.0 · GHC.Internal.Genericstype Rep1 Proxy = D1 ('MetaData "Proxy"
"GHC.Internal.Data.Proxy"
"ghc-internal"
'False) (C1 ('MetaCons "Proxy"
'PrefixI 'False) U1)Defined in ghc-internal-10.0.0 · GHC.Internal.Generics