packages feed

aztecs 0.4.0.1 → 0.5.0.0

raw patch · 14 files changed

+325/−319 lines, 14 filesdep +parallel-iodep ~basedep ~containersPVP ok

version bump matches the API change (PVP)

Dependencies added: parallel-io

Dependency ranges changed: base, containers

API changes (from Hackage documentation)

- Data.Aztecs: SystemT :: (Components -> (Components, ReadsWrites, DynamicSystemT m i o)) -> SystemT m i o
- Data.Aztecs: [runSystem'] :: SystemT m i o -> Components -> (Components, ReadsWrites, DynamicSystemT m i o)
- Data.Aztecs: newtype SystemT m i o
- Data.Aztecs: runSystem :: System () () -> IO World
- Data.Aztecs: runSystem_ :: System () () -> IO ()
- Data.Aztecs.Query: run :: Monad m => (i -> m o) -> Query m i o
- Data.Aztecs.System: [runSystem'] :: SystemT m i o -> Components -> (Components, ReadsWrites, DynamicSystemT m i o)
- Data.Aztecs.System: [runSystemDyn] :: DynamicSystemT m i o -> i -> TVar World -> m (o, Access m ())
- Data.Aztecs.System: forever :: System () () -> System () ()
- Data.Aztecs.System: instance Control.Arrow.Arrow Data.Aztecs.System.DynamicSystem
- Data.Aztecs.System: instance Control.Arrow.Arrow Data.Aztecs.System.System
- Data.Aztecs.System: instance Control.Arrow.ArrowLoop Data.Aztecs.System.DynamicSystem
- Data.Aztecs.System: instance Control.Arrow.ArrowLoop Data.Aztecs.System.System
- Data.Aztecs.System: instance Control.Category.Category Data.Aztecs.System.DynamicSystem
- Data.Aztecs.System: instance Control.Category.Category Data.Aztecs.System.System
- Data.Aztecs.System: instance GHC.Base.Monad m => GHC.Base.Applicative (Data.Aztecs.System.DynamicSystemT m i)
- Data.Aztecs.System: instance GHC.Base.Monad m => GHC.Base.Applicative (Data.Aztecs.System.SystemT m i)
- Data.Aztecs.System: mapDyn_ :: Set ComponentID -> DynamicQuery IO () a -> DynamicSystem () ()
- Data.Aztecs.System: run :: Monad m => (i -> m o) -> SystemT m i o
- Data.Aztecs.System: runDyn :: Monad m => (i -> m o) -> DynamicSystemT m i o
- Data.Aztecs.System: runSystem :: System () () -> IO World
- Data.Aztecs.System: runSystemWithWorld :: System () () -> World -> IO World
- Data.Aztecs.System: runSystem_ :: System () () -> IO ()
- Data.Aztecs.System: type DynamicSystem = DynamicSystemT IO
+ Data.Aztecs: data Schedule m i o
+ Data.Aztecs: data SystemT m i o
+ Data.Aztecs: forever :: Monad m => Schedule m i () -> Schedule m i ()
+ Data.Aztecs: runSchedule :: Monad m => Schedule m i o -> World -> i -> m (o, World)
+ Data.Aztecs: runSchedule_ :: Monad m => Schedule m () () -> m ()
+ Data.Aztecs: schedule :: Monad m => SystemT m i o -> Schedule m i o
+ Data.Aztecs.Query: task :: Monad m => (i -> m o) -> Query m i o
+ Data.Aztecs.Schedule: Schedule :: (Components -> (i -> Access m o, Components)) -> Schedule m i o
+ Data.Aztecs.Schedule: [runSchedule'] :: Schedule m i o -> Components -> (i -> Access m o, Components)
+ Data.Aztecs.Schedule: forever :: Monad m => Schedule m i () -> Schedule m i ()
+ Data.Aztecs.Schedule: instance Control.Arrow.Arrow (Data.Aztecs.Schedule.Schedule GHC.Types.IO)
+ Data.Aztecs.Schedule: instance GHC.Base.Monad m => Control.Category.Category (Data.Aztecs.Schedule.Schedule m)
+ Data.Aztecs.Schedule: newtype Schedule m i o
+ Data.Aztecs.Schedule: runSchedule :: Monad m => Schedule m i o -> World -> i -> m (o, World)
+ Data.Aztecs.Schedule: runSchedule_ :: Monad m => Schedule m () () -> m ()
+ Data.Aztecs.Schedule: schedule :: Monad m => SystemT m i o -> Schedule m i o
+ Data.Aztecs.System: [runSystemTDyn] :: DynamicSystemT m i o -> World -> i -> m (o, View, Access m ())
+ Data.Aztecs.System: [runSystemT] :: SystemT m i o -> Components -> (DynamicSystemT m i o, ReadsWrites, Components)
+ Data.Aztecs.System: filterDyn :: Monad m => Set ComponentID -> DynamicQuery m i o -> (Node -> Bool) -> DynamicSystemT m i [o]
+ Data.Aztecs.System: filterMapDyn :: Monad m => Set ComponentID -> DynamicQuery m i o -> (Node -> Bool) -> DynamicSystemT m i [o]
+ Data.Aztecs.System: instance Control.Arrow.Arrow (Data.Aztecs.System.SystemT GHC.Types.IO)
+ Data.Aztecs.System: instance GHC.Base.Monad m => Control.Arrow.Arrow (Data.Aztecs.System.DynamicSystemT m)
+ Data.Aztecs.System: instance GHC.Base.Monad m => Control.Category.Category (Data.Aztecs.System.DynamicSystemT m)
+ Data.Aztecs.System: instance GHC.Base.Monad m => Control.Category.Category (Data.Aztecs.System.SystemT m)
+ Data.Aztecs.System: raceDyn :: DynamicSystemT IO i a -> DynamicSystemT IO i b -> DynamicSystemT IO i (a, b)
+ Data.Aztecs.System: task :: Monad m => (i -> m o) -> SystemT m i o
- Data.Aztecs: type System = SystemT IO
+ Data.Aztecs: type System i o = SystemT IO i o
- Data.Aztecs.Query: DynamicQuery :: ([i] -> [EntityID] -> Archetype -> m ([o], Archetype)) -> (i -> EntityID -> Archetype -> m (Maybe o, Archetype)) -> DynamicQuery m i o
+ Data.Aztecs.Query: DynamicQuery :: ![i] -> [EntityID] -> Archetype -> m ([o], Archetype) -> !i -> EntityID -> Archetype -> m (Maybe o, Archetype) -> DynamicQuery m i o
- Data.Aztecs.Query: DynamicQueryFilter :: Set ComponentID -> Set ComponentID -> DynamicQueryFilter
+ Data.Aztecs.Query: DynamicQueryFilter :: !Set ComponentID -> !Set ComponentID -> DynamicQueryFilter
- Data.Aztecs.Query: ReadsWrites :: Set ComponentID -> Set ComponentID -> ReadsWrites
+ Data.Aztecs.Query: ReadsWrites :: !Set ComponentID -> !Set ComponentID -> ReadsWrites
- Data.Aztecs.Query: [dynQueryAll] :: DynamicQuery m i o -> [i] -> [EntityID] -> Archetype -> m ([o], Archetype)
+ Data.Aztecs.Query: [dynQueryAll] :: DynamicQuery m i o -> ![i] -> [EntityID] -> Archetype -> m ([o], Archetype)
- Data.Aztecs.Query: [dynQueryLookup] :: DynamicQuery m i o -> i -> EntityID -> Archetype -> m (Maybe o, Archetype)
+ Data.Aztecs.Query: [dynQueryLookup] :: DynamicQuery m i o -> !i -> EntityID -> Archetype -> m (Maybe o, Archetype)
- Data.Aztecs.Query: [filterWith] :: DynamicQueryFilter -> Set ComponentID
+ Data.Aztecs.Query: [filterWith] :: DynamicQueryFilter -> !Set ComponentID
- Data.Aztecs.Query: [filterWithout] :: DynamicQueryFilter -> Set ComponentID
+ Data.Aztecs.Query: [filterWithout] :: DynamicQueryFilter -> !Set ComponentID
- Data.Aztecs.Query: [reads] :: ReadsWrites -> Set ComponentID
+ Data.Aztecs.Query: [reads] :: ReadsWrites -> !Set ComponentID
- Data.Aztecs.Query: [writes] :: ReadsWrites -> Set ComponentID
+ Data.Aztecs.Query: [writes] :: ReadsWrites -> !Set ComponentID
- Data.Aztecs.System: DynamicSystemT :: (i -> TVar World -> m (o, Access m ())) -> DynamicSystemT m i o
+ Data.Aztecs.System: DynamicSystemT :: (World -> i -> m (o, View, Access m ())) -> DynamicSystemT m i o
- Data.Aztecs.System: SystemT :: (Components -> (Components, ReadsWrites, DynamicSystemT m i o)) -> SystemT m i o
+ Data.Aztecs.System: SystemT :: (Components -> (DynamicSystemT m i o, ReadsWrites, Components)) -> SystemT m i o
- Data.Aztecs.System: all :: Query IO () a -> System () [a]
+ Data.Aztecs.System: all :: Monad m => Query m i a -> SystemT m i [a]
- Data.Aztecs.System: allDyn :: Set ComponentID -> DynamicQuery IO () a -> DynamicSystem () [a]
+ Data.Aztecs.System: allDyn :: Monad m => Set ComponentID -> DynamicQuery m i o -> DynamicSystemT m i [o]
- Data.Aztecs.System: filter :: Query IO () a -> QueryFilter -> System () [a]
+ Data.Aztecs.System: filter :: Monad m => Query m () a -> QueryFilter -> SystemT m () [a]
- Data.Aztecs.System: filterMap :: Query IO i a -> QueryFilter -> System i [a]
+ Data.Aztecs.System: filterMap :: Monad m => Query m i a -> QueryFilter -> SystemT m i [a]
- Data.Aztecs.System: map :: Query IO i a -> System i [a]
+ Data.Aztecs.System: map :: Monad m => Query m i a -> SystemT m i [a]
- Data.Aztecs.System: mapDyn :: Set ComponentID -> DynamicQuery IO i a -> DynamicSystem i [a]
+ Data.Aztecs.System: mapDyn :: Monad m => Set ComponentID -> DynamicQuery m i o -> DynamicSystemT m i [o]
- Data.Aztecs.System: mapSingle :: Query IO i a -> System i a
+ Data.Aztecs.System: mapSingle :: Monad m => Query m i a -> SystemT m i a
- Data.Aztecs.System: map_ :: Query IO i a -> System i ()
+ Data.Aztecs.System: map_ :: Monad m => Query m i o -> SystemT m i ()
- Data.Aztecs.System: queueDyn :: Applicative m => (i -> Access m ()) -> DynamicSystemT m i ()
+ Data.Aztecs.System: queueDyn :: Monad m => (i -> Access m ()) -> DynamicSystemT m i ()
- Data.Aztecs.System: single :: Query IO () a -> System () a
+ Data.Aztecs.System: single :: Monad m => Query m () a -> SystemT m () a
- Data.Aztecs.System: singleDyn :: Set ComponentID -> DynamicQuery IO () a -> DynamicSystem () a
+ Data.Aztecs.System: singleDyn :: Monad m => Set ComponentID -> DynamicQuery m () a -> DynamicSystemT m () a
- Data.Aztecs.System: type System = SystemT IO
+ Data.Aztecs.System: type System i o = SystemT IO i o
- Data.Aztecs.World: World :: Archetypes -> Components -> Map EntityID ArchetypeID -> EntityID -> World
+ Data.Aztecs.World: World :: !Archetypes -> !Components -> !Map EntityID ArchetypeID -> !EntityID -> World
- Data.Aztecs.World: [archetypes] :: World -> Archetypes
+ Data.Aztecs.World: [archetypes] :: World -> !Archetypes
- Data.Aztecs.World: [components] :: World -> Components
+ Data.Aztecs.World: [components] :: World -> !Components
- Data.Aztecs.World: [entities] :: World -> Map EntityID ArchetypeID
+ Data.Aztecs.World: [entities] :: World -> !Map EntityID ArchetypeID
- Data.Aztecs.World: [nextEntityId] :: World -> EntityID
+ Data.Aztecs.World: [nextEntityId] :: World -> !EntityID
- Data.Aztecs.World.Archetype: AnyStorage :: Dynamic -> (Int -> Dynamic -> Dynamic -> Dynamic) -> (Int -> Dynamic -> (Maybe Dynamic, Dynamic)) -> (Int -> Dynamic -> (Maybe AnyStorage, Dynamic)) -> (Dynamic -> [Int]) -> AnyStorage
+ Data.Aztecs.World.Archetype: AnyStorage :: !Dynamic -> !Int -> Dynamic -> Dynamic -> Dynamic -> !Int -> Dynamic -> (Maybe Dynamic, Dynamic) -> !Int -> Dynamic -> (Maybe AnyStorage, Dynamic) -> !Dynamic -> [Int] -> AnyStorage
- Data.Aztecs.World.Archetype: [entitiesDyn] :: AnyStorage -> Dynamic -> [Int]
+ Data.Aztecs.World.Archetype: [entitiesDyn] :: AnyStorage -> !Dynamic -> [Int]
- Data.Aztecs.World.Archetype: [insertDyn] :: AnyStorage -> Int -> Dynamic -> Dynamic -> Dynamic
+ Data.Aztecs.World.Archetype: [insertDyn] :: AnyStorage -> !Int -> Dynamic -> Dynamic -> Dynamic
- Data.Aztecs.World.Archetype: [removeAny] :: AnyStorage -> Int -> Dynamic -> (Maybe AnyStorage, Dynamic)
+ Data.Aztecs.World.Archetype: [removeAny] :: AnyStorage -> !Int -> Dynamic -> (Maybe AnyStorage, Dynamic)
- Data.Aztecs.World.Archetype: [removeDyn] :: AnyStorage -> Int -> Dynamic -> (Maybe Dynamic, Dynamic)
+ Data.Aztecs.World.Archetype: [removeDyn] :: AnyStorage -> !Int -> Dynamic -> (Maybe Dynamic, Dynamic)
- Data.Aztecs.World.Archetype: [storageDyn] :: AnyStorage -> Dynamic
+ Data.Aztecs.World.Archetype: [storageDyn] :: AnyStorage -> !Dynamic
- Data.Aztecs.World.Archetypes: Archetypes :: Map ArchetypeID Node -> Map (Set ComponentID) ArchetypeID -> ArchetypeID -> Map ComponentID (Set ArchetypeID) -> Archetypes
+ Data.Aztecs.World.Archetypes: Archetypes :: !Map ArchetypeID Node -> !Map (Set ComponentID) ArchetypeID -> !ArchetypeID -> !Map ComponentID (Set ArchetypeID) -> Archetypes
- Data.Aztecs.World.Archetypes: Node :: Set ComponentID -> Archetype -> Map ComponentID ArchetypeID -> Map ComponentID ArchetypeID -> Node
+ Data.Aztecs.World.Archetypes: Node :: !Set ComponentID -> !Archetype -> !Map ComponentID ArchetypeID -> !Map ComponentID ArchetypeID -> Node
- Data.Aztecs.World.Archetypes: [archetypeIds] :: Archetypes -> Map (Set ComponentID) ArchetypeID
+ Data.Aztecs.World.Archetypes: [archetypeIds] :: Archetypes -> !Map (Set ComponentID) ArchetypeID
- Data.Aztecs.World.Archetypes: [componentIds] :: Archetypes -> Map ComponentID (Set ArchetypeID)
+ Data.Aztecs.World.Archetypes: [componentIds] :: Archetypes -> !Map ComponentID (Set ArchetypeID)
- Data.Aztecs.World.Archetypes: [nextArchetypeId] :: Archetypes -> ArchetypeID
+ Data.Aztecs.World.Archetypes: [nextArchetypeId] :: Archetypes -> !ArchetypeID
- Data.Aztecs.World.Archetypes: [nodeAdd] :: Node -> Map ComponentID ArchetypeID
+ Data.Aztecs.World.Archetypes: [nodeAdd] :: Node -> !Map ComponentID ArchetypeID
- Data.Aztecs.World.Archetypes: [nodeArchetype] :: Node -> Archetype
+ Data.Aztecs.World.Archetypes: [nodeArchetype] :: Node -> !Archetype
- Data.Aztecs.World.Archetypes: [nodeComponentIds] :: Node -> Set ComponentID
+ Data.Aztecs.World.Archetypes: [nodeComponentIds] :: Node -> !Set ComponentID
- Data.Aztecs.World.Archetypes: [nodeRemove] :: Node -> Map ComponentID ArchetypeID
+ Data.Aztecs.World.Archetypes: [nodeRemove] :: Node -> !Map ComponentID ArchetypeID
- Data.Aztecs.World.Archetypes: [nodes] :: Archetypes -> Map ArchetypeID Node
+ Data.Aztecs.World.Archetypes: [nodes] :: Archetypes -> !Map ArchetypeID Node
- Data.Aztecs.World.Components: Components :: Map TypeRep ComponentID -> ComponentID -> Components
+ Data.Aztecs.World.Components: Components :: !Map TypeRep ComponentID -> !ComponentID -> Components
- Data.Aztecs.World.Components: [componentIds] :: Components -> Map TypeRep ComponentID
+ Data.Aztecs.World.Components: [componentIds] :: Components -> !Map TypeRep ComponentID
- Data.Aztecs.World.Components: [nextComponentId] :: Components -> ComponentID
+ Data.Aztecs.World.Components: [nextComponentId] :: Components -> !ComponentID

Files

aztecs.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name:          aztecs-version:       0.4.0.1+version:       0.5.0.0 license:       BSD-3-Clause license-file:  LICENSE maintainer:    matt@hunzinger.me@@ -26,6 +26,7 @@         Data.Aztecs.Component         Data.Aztecs.Entity         Data.Aztecs.Query+        Data.Aztecs.Schedule         Data.Aztecs.Storage         Data.Aztecs.System         Data.Aztecs.View@@ -33,14 +34,14 @@         Data.Aztecs.World.Archetype         Data.Aztecs.World.Archetypes         Data.Aztecs.World.Components-     hs-source-dirs:   src     default-language: Haskell2010     ghc-options:      -Wall     build-depends:-        base >=4 && <5,-        containers >=0.7,-        mtl >=2+        base >=4.6 && <5,+        containers >=0.6,+        mtl >=2,+        parallel-io >=0.3.5  executable ecs     main-is:          ECS.hs@@ -48,7 +49,7 @@     default-language: Haskell2010     ghc-options:      -Wall     build-depends:-        base >=4 && <5,+        base >=4.6 && <5,         aztecs  test-suite aztecs-test@@ -58,7 +59,7 @@     default-language: Haskell2010     ghc-options:      -Wall     build-depends:-        base >=4 && <5,+        base >=4.6 && <5,         aztecs,         hspec >=2,         QuickCheck >=2@@ -70,6 +71,6 @@     default-language: Haskell2010     ghc-options:      -Wall     build-depends:-        base >=4 && <5,+        base >=4.6 && <5,         aztecs,         criterion >=1
examples/ECS.hs view
@@ -1,6 +1,4 @@ {-# LANGUAGE Arrows #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE TypeApplications #-}  module Main where @@ -29,7 +27,10 @@         Position p <- Q.fetch -< ()         Q.set -< Position $ p + v     )-    >>> S.run print+    >>> S.task print +app :: Schedule IO () ()+app = schedule setup >>> forever (schedule move)+ main :: IO ()-main = runSystem_ $ setup >>> S.forever move+main = runSchedule_ $ app
src/Data/Aztecs.hs view
@@ -56,14 +56,17 @@     bundle,     Component (..),     EntityID,-    System,-    SystemT (..),     Query,     QueryFilter,     with,     without,-    runSystem,-    runSystem_,+    System,+    SystemT,+    Schedule,+    schedule,+    forever,+    runSchedule,+    runSchedule_,     World,   ) where@@ -72,6 +75,7 @@ import Data.Aztecs.Component (Component (..)) import Data.Aztecs.Entity (EntityID) import Data.Aztecs.Query (Query, QueryFilter, with, without)-import Data.Aztecs.System (System, SystemT (..), runSystem, runSystem_)+import Data.Aztecs.Schedule (Schedule, forever, runSchedule, runSchedule_, schedule)+import Data.Aztecs.System (System, SystemT) import Data.Aztecs.World (World) import Data.Aztecs.World.Archetype (Bundle, bundle)
src/Data/Aztecs/Access.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE BangPatterns #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE RankNTypes #-}@@ -16,7 +17,7 @@ where  import Control.Monad.IO.Class (MonadIO)-import Control.Monad.State (MonadState (..), StateT (..))+import Control.Monad.State.Strict (MonadState (..), StateT (..)) import Data.Aztecs.Component (Component (..)) import Data.Aztecs.Entity (EntityID (..)) import Data.Aztecs.World (World (..))@@ -39,8 +40,8 @@   Bundle ->   Access m EntityID spawn c = Access $ do-  w <- get-  let (e, w') = W.spawn c w+  !w <- get+  let !(e, w') = W.spawn c w   put w'   return e @@ -52,17 +53,17 @@ -- | Insert a component into an entity. insert :: (Monad m, Component a, Typeable (StorageT a)) => EntityID -> a -> Access m () insert e c = Access $ do-  w <- get-  let w' = W.insert e c w+  !w <- get+  let !w' = W.insert e c w   put w'  lookup :: (Monad m, Component a) => EntityID -> Access m (Maybe a) lookup e = Access $ do-  w <- get+  !w <- get   return $ W.lookup e w  despawn :: (Monad m) => EntityID -> Access m () despawn e = Access $ do-  w <- get-  let (_, w') = W.despawn e w+  !w <- get+  let !(_, w') = W.despawn e w   put w'
src/Data/Aztecs/Component.hs view
@@ -4,7 +4,7 @@ module Data.Aztecs.Component (Component (..), ComponentID (..)) where  import Data.Aztecs.Storage (Storage)-import Data.IntMap (IntMap)+import Data.IntMap.Strict (IntMap) import Data.Kind (Type) import Data.Typeable (Typeable) 
src/Data/Aztecs/Query.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE BangPatterns #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeApplications #-}@@ -11,7 +12,7 @@     fetch,     fetchMaybe,     set,-    run,+    task,     all,      -- * Filters@@ -45,7 +46,7 @@ import qualified Data.Aztecs.World.Archetypes as AS import Data.Aztecs.World.Components (Components) import qualified Data.Aztecs.World.Components as CS-import qualified Data.Map as Map+import qualified Data.Map.Strict as Map import Data.Set (Set) import qualified Data.Set as Set import Prelude hiding (all, any, id, lookup, map, mapM, reads, (.))@@ -113,8 +114,8 @@    in (ReadsWrites Set.empty (Set.singleton cId), cs', setDyn cId)  -- | Run a monadic task in a `Query`.-run :: (Monad m) => (i -> m o) -> Query m i o-run f = Query $ \cs ->+task :: (Monad m) => (i -> m o) -> Query m i o+task f = Query $ \cs ->   ( mempty,     cs,     DynamicQuery@@ -129,7 +130,7 @@ -- >>> import Data.Aztecs -- >>> import qualified Data.Aztecs.World as W -- >>>--- >>> data X = X Int deriving (Show)+-- >>> newtype X = X Int deriving (Show) -- >>> instance Component X -- >>> -- >>> let (_, w) = W.spawn (bundle $ X 0) W.empty@@ -146,8 +147,8 @@   return (concat as, w {components = cs'})  data ReadsWrites = ReadsWrites-  { reads :: Set ComponentID,-    writes :: Set ComponentID+  { reads :: !(Set ComponentID),+    writes :: !(Set ComponentID)   }   deriving (Show) @@ -189,8 +190,8 @@   let (cId, cs') = CS.insert @a cs in (mempty {filterWithout = Set.singleton cId}, cs')  data DynamicQueryFilter = DynamicQueryFilter-  { filterWith :: Set ComponentID,-    filterWithout :: Set ComponentID+  { filterWith :: !(Set ComponentID),+    filterWithout :: !(Set ComponentID)   }  instance Semigroup DynamicQueryFilter where@@ -202,8 +203,8 @@  -- | Dynamic query for components by ID. data DynamicQuery m i o = DynamicQuery-  { dynQueryAll :: [i] -> [EntityID] -> Archetype -> m ([o], Archetype),-    dynQueryLookup :: i -> EntityID -> Archetype -> m (Maybe o, Archetype)+  { dynQueryAll :: !([i] -> [EntityID] -> Archetype -> m ([o], Archetype)),+    dynQueryLookup :: !(i -> EntityID -> Archetype -> m (Maybe o, Archetype))   }  instance (Functor m) => Functor (DynamicQuery m i) where@@ -287,7 +288,7 @@ fetchDyn :: forall m a. (Applicative m, Component a) => ComponentID -> DynamicQuery m () a fetchDyn cId =   DynamicQuery-    { dynQueryAll = \_ _ arch -> let as = A.all cId arch in pure (fmap snd as, arch),+    { dynQueryAll = \_ _ arch -> let !as = A.all cId arch in pure (fmap snd as, arch),       dynQueryLookup = \_ eId arch -> pure $ (A.lookupComponent eId cId arch, arch)     } @@ -311,7 +312,7 @@   DynamicQuery m a a setDyn cId =   DynamicQuery-    { dynQueryAll = \is _ arch -> pure (is, A.withAscList cId is arch),+    { dynQueryAll = \is _ arch -> let !arch' = A.withAscList cId is arch in pure (is, arch'),       dynQueryLookup =         \i eId arch -> pure (A.lookupComponent eId cId arch, A.insertComponent eId cId i arch)     }
+ src/Data/Aztecs/Schedule.hs view
@@ -0,0 +1,70 @@+{-# LANGUAGE FlexibleInstances #-}++module Data.Aztecs.Schedule+  ( -- * Schedules+    Schedule (..),+    schedule,+    forever,+    runSchedule,+    runSchedule_,+  )+where++import Control.Arrow (Arrow (..))+import Control.Category (Category (..))+import Control.Monad ((>=>))+import Control.Monad.State (MonadState (..))+import Control.Monad.Trans (MonadTrans (..))+import Data.Aztecs.Access (Access (..), runAccess)+import Data.Aztecs.System (DynamicSystemT (..), SystemT (..))+import qualified Data.Aztecs.View as V+import Data.Aztecs.World (World (..))+import qualified Data.Aztecs.World as W+import Data.Aztecs.World.Components (Components)++newtype Schedule m i o = Schedule {runSchedule' :: Components -> (i -> Access m o, Components)}++instance (Monad m) => Category (Schedule m) where+  id = Schedule $ \cs -> (return, cs)+  Schedule f . Schedule g = Schedule $ \cs ->+    let (g', cs') = g cs+        (f', cs'') = f cs'+     in (g' >=> f', cs'')++instance Arrow (Schedule IO) where+  arr f = Schedule $ \cs -> (return Prelude.. f, cs)+  first (Schedule f) = Schedule $ \cs ->+    let (g, cs') = f cs+     in (\(b, d) -> (,) <$> g b <*> return d, cs')++runSchedule :: (Monad m) => Schedule m i o -> World -> i -> m (o, World)+runSchedule s w i = do+  let (f, cs) = runSchedule' s (components w)+  (o, w') <- runAccess (f i) w {components = cs}+  return (o, w')++runSchedule_ :: (Monad m) => Schedule m () () -> m ()+runSchedule_ s = const () <$> runSchedule s (W.empty) ()++schedule :: (Monad m) => SystemT m i o -> Schedule m i o+schedule t = Schedule $ \cs ->+  let (dynT, _, cs') = runSystemT t cs+      go i = Access $ do+        w <- get+        let f = runSystemTDyn dynT w+        (o, v, access) <- lift $ f i+        ((), w') <- lift Prelude.. runAccess access $ V.unview v w+        put w'+        return o+   in (go, cs')++forever :: (Monad m) => Schedule m i () -> Schedule m i ()+forever s = Schedule $ \cs ->+  let (f, cs') = runSchedule' s cs+      go i = Access $ do+        w <- get+        let loop wAcc = do+              ((), wAcc') <- lift $ runAccess (f i) wAcc+              loop wAcc'+        loop w+   in (go, cs')
src/Data/Aztecs/Storage.hs view
@@ -5,8 +5,8 @@ module Data.Aztecs.Storage (Storage (..)) where  import Data.Data (Typeable)-import Data.IntMap (IntMap)-import qualified Data.IntMap as IntMap+import Data.IntMap.Strict (IntMap)+import qualified Data.IntMap.Strict as IntMap  -- | Component storage, containing zero or many components of the same type. class (Typeable (s a), Typeable a) => Storage s a where
src/Data/Aztecs/System.hs view
@@ -1,18 +1,14 @@+{-# LANGUAGE BangPatterns #-} {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE RecursiveDo #-}-{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-}  module Data.Aztecs.System   ( -- * Systems     System,     SystemT (..),-    forever,-    run,     queue,+    task,      -- ** Queries @@ -24,122 +20,91 @@     -- *** Writing     map,     map_,-    mapSingle,     filterMap,--    -- ** Running-    runSystem,-    runSystem_,-    runSystemWithWorld,+    mapSingle, -    -- * Dynamic systems-    DynamicSystem,+    -- * Dynamic SystemTs     DynamicSystemT (..),+    queueDyn,+    raceDyn,++    -- ** Queries++    -- *** Reading     allDyn,+    filterDyn,     singleDyn,++    -- *** Writing     mapDyn,-    mapDyn_,-    queueDyn,-    runDyn,+    filterMapDyn,   ) where -import Control.Arrow (Arrow (..), ArrowLoop (..))+import Control.Arrow (Arrow (..)) import Control.Category (Category (..))-import Control.Concurrent (forkIO)-import Data.Aztecs.Access (Access, runAccess)+import Control.Concurrent.ParallelIO.Global+import Data.Aztecs.Access (Access (..))+import Data.Aztecs.Component (ComponentID) import Data.Aztecs.Query (DynamicQuery, DynamicQueryFilter (..), Query (..), QueryFilter (..), ReadsWrites) import qualified Data.Aztecs.Query as Q+import Data.Aztecs.View (View) import qualified Data.Aztecs.View as V import Data.Aztecs.World (World (..))-import qualified Data.Aztecs.World as W import qualified Data.Aztecs.World.Archetype as A-import Data.Aztecs.World.Archetypes (Node (nodeArchetype))-import Data.Aztecs.World.Components (ComponentID, Components)+import Data.Aztecs.World.Archetypes (Node (..))+import Data.Aztecs.World.Components (Components) import qualified Data.Foldable as F import Data.Set (Set)-import GHC.Conc (TVar, atomically, newTVarIO, readTVarIO, writeTVar)-import Prelude hiding (all, filter, id, map, (.))+import Prelude hiding (all, filter, map, (.))+import qualified Prelude hiding (filter, map) -type System = SystemT IO+type System i o = SystemT IO i o --- | System that can access and alter a `World`.------ Systems can be composed either in sequence or parallel with `Category` and `Arrow` combinators.--- Using the arrow combinator `&&&`, systems will automatically run in parallel--- as long as their queries don't intersect.-newtype SystemT m i o = SystemT-  { -- | Initialize a system, producing a `DynamicSystem`.-    runSystem' :: Components -> (Components, ReadsWrites, DynamicSystemT m i o)-  }+newtype SystemT m i o = SystemT {runSystemT :: Components -> (DynamicSystemT m i o, ReadsWrites, Components)}   deriving (Functor) -instance (Monad m) => Applicative (SystemT m i) where-  pure a = SystemT (,mempty,pure a)-  f <*> a =-    SystemT $ \w ->-      let (w', cIds, f') = runSystem' f w-          (w'', cIds', a') = runSystem' a w'-       in (w'', cIds <> cIds', f' <*> a')--instance Category System where-  id = SystemT (,mempty,id)-  f . g = SystemT $ \cs ->-    let (cs', cIds, g') = runSystem' g cs-        (cs'', cIds', f') = runSystem' f cs'-     in (cs'', cIds <> cIds', f' . g')--instance Arrow System where-  arr f = SystemT (,mempty,arr f)-  first s = SystemT $ \w -> let (w', cIds, dynS) = runSystem' s w in (w', cIds, first dynS)-  a &&& b = SystemT $ \w ->-    let (w', aRws, dynA) = runSystem' a w-        (w'', bRws, dynB) = runSystem' b w'-        f = if Q.disjoint aRws bRws then (&&&) else joinDyn-     in (w'', aRws <> bRws, f dynA dynB)--instance ArrowLoop System where-  loop s = SystemT $ \w -> let (w', cIds, dynS) = runSystem' s w in (w', cIds, loop dynS)---- | Run a system forever.-forever :: System () () -> System () ()-forever s = SystemT $ \w -> let (w', cIds, dynS) = runSystem' s w in (w', cIds, foreverDyn dynS)--runSystem_ :: System () () -> IO ()-runSystem_ s = runSystem s >> pure ()--runSystem :: System () () -> IO World-runSystem s = runSystemWithWorld s W.empty+instance (Monad m) => Category (SystemT m) where+  id = SystemT $ \cs -> (DynamicSystemT $ \_ -> \i -> return (i, mempty, pure ()), mempty, cs)+  SystemT f . SystemT g = SystemT $ \cs ->+    let (f', rwsF, cs') = f cs+        (g', rwsG, cs'') = g cs'+     in (f' . g', rwsF <> rwsG, cs'') -runSystemWithWorld :: System () () -> World -> IO World-runSystemWithWorld s w = do-  let (cs, _, dynS) = runSystem' s (components w)-      w' = w {components = cs}-  wVar <- newTVarIO w'-  ((), access) <- runSystemDyn dynS () wVar-  w'' <- readTVarIO wVar-  ((), w''') <- runAccess access w''-  return w'''+instance Arrow (SystemT IO) where+  arr f = SystemT $ \cs -> (DynamicSystemT $ \_ -> \i -> return (f i, mempty, pure ()), mempty, cs)+  first (SystemT f) = SystemT $ \cs ->+    let (f', rwsF, cs') = f cs+     in (first f', rwsF, cs')+  a &&& b = SystemT $ \cs ->+    let (dynA, rwsA, cs') = runSystemT a cs+        (dynB, rwsB, cs'') = runSystemT b cs'+     in ( if Q.disjoint rwsA rwsB+            then dynA &&& dynB+            else raceDyn dynA dynB,+          rwsA <> rwsB,+          cs''+        )  -- | Query all matching entities.-all :: Query IO () a -> System () [a]+all :: (Monad m) => Query m i a -> SystemT m i [a] all q = SystemT $ \cs ->-  let (rws, cs', dynQ) = runQuery q cs-   in (cs', rws, allDyn (Q.reads rws <> Q.writes rws) dynQ)+  let !(rws, cs', dynQ) = runQuery q cs+   in (allDyn (Q.reads rws <> Q.writes rws) dynQ, rws, cs')  -- | Query all matching entities with a `QueryFilter`.-filter :: Query IO () a -> QueryFilter -> System () [a]+filter :: (Monad m) => Query m () a -> QueryFilter -> SystemT m () [a] filter q qf = SystemT $ \cs ->-  let (rws, cs', dynQ) = runQuery q cs-      (dynQf, cs'') = runQueryFilter qf cs'+  let !(rws, cs', dynQ) = runQuery q cs+      !(dynQf, cs'') = runQueryFilter qf cs'       qf' n =         F.all (\cId -> A.member cId $ nodeArchetype n) (filterWith dynQf)           && F.all (\cId -> not (A.member cId $ nodeArchetype n)) (filterWithout dynQf)-   in (cs'', rws, filterDyn (Q.reads rws <> Q.writes rws) dynQ qf')+   in (filterDyn (Q.reads rws <> Q.writes rws) dynQ qf', rws, cs'')  -- | Query a single matching entity. -- If there are zero or multiple matching entities, an error will be thrown.-single :: Query IO () a -> System () a+single :: (Monad m) => Query m () a -> SystemT m () a single q =   fmap     ( \as -> case as of@@ -148,28 +113,28 @@     )     (all q) --- | Map all matching entities, storing the updated entities.-map :: Query IO i a -> System i [a]+-- | Query all matching entities.+map :: (Monad m) => Query m i a -> SystemT m i [a] map q = SystemT $ \cs ->-  let (rws, cs', dynS) = runQuery q cs in (cs', rws, mapDyn (Q.reads rws <> Q.writes rws) dynS)+  let !(rws, cs', dynQ) = runQuery q cs+   in (mapDyn (Q.reads rws <> Q.writes rws) dynQ, rws, cs') --- | Map all matching entities and ignore the output, storing the updated entities.-map_ :: Query IO i a -> System i ()+map_ :: (Monad m) => Query m i o -> SystemT m i () map_ q = const () <$> map q  -- | Map all matching entities with a `QueryFilter`, storing the updated entities.-filterMap :: Query IO i a -> QueryFilter -> System i [a]+filterMap :: (Monad m) => Query m i a -> QueryFilter -> SystemT m i [a] filterMap q qf = SystemT $ \cs ->-  let (rws, cs', dynQ) = runQuery q cs-      (dynQf, cs'') = runQueryFilter qf cs'+  let !(rws, cs', dynQ) = runQuery q cs+      !(dynQf, cs'') = runQueryFilter qf cs'       f' n =         F.all (\cId -> A.member cId $ nodeArchetype n) (filterWith dynQf)           && F.all (\cId -> not (A.member cId $ nodeArchetype n)) (filterWithout dynQf)-   in (cs'', rws, filterMapDyn (Q.reads rws <> Q.writes rws) dynQ f')+   in (filterMapDyn (Q.reads rws <> Q.writes rws) dynQ f', rws, cs'')  -- | Map a single matching entity, storing the updated components. -- If there are zero or multiple matching entities, an error will be thrown.-mapSingle :: Query IO i a -> System i a+mapSingle :: (Monad m) => Query m i a -> SystemT m i a mapSingle q =   fmap     ( \as -> case as of@@ -178,98 +143,46 @@     )     (map q) --- | Queue an `Access` to alter the world after this system is complete. queue :: (Monad m) => (i -> Access m ()) -> SystemT m i ()-queue f = SystemT (,mempty,queueDyn f)---- | Run a monadic task.-run :: (Monad m) => (i -> m o) -> SystemT m i o-run f = SystemT (,mempty,runDyn f)+queue f = SystemT $ \cs -> (queueDyn f, mempty, cs) -type DynamicSystem = DynamicSystemT IO+task :: (Monad m) => (i -> m o) -> SystemT m i o+task f = SystemT $ \cs ->+  ( DynamicSystemT $ \_ -> \i -> do+      o <- f i+      return (o, mempty, pure ()),+    mempty,+    cs+  ) --- | Dynamic system that can access and alter a `World`.-newtype DynamicSystemT m i o = DynamicSystemT-  {runSystemDyn :: i -> TVar World -> m (o, Access m ())}+newtype DynamicSystemT m i o = DynamicSystemT {runSystemTDyn :: World -> (i -> m (o, View, Access m ()))}   deriving (Functor) -instance (Monad m) => Applicative (DynamicSystemT m i) where-  pure a = DynamicSystemT $ \_ _ -> pure (a, pure ())-  f <*> a =-    DynamicSystemT $ \i w -> do-      (f'', access) <- runSystemDyn f i w-      (a'', access') <- runSystemDyn a i w-      return (f'' a'', access >> access')--instance Category DynamicSystem where-  id = DynamicSystemT $ \i _ -> pure (i, pure ())-  f . g = DynamicSystemT $ \i wVar -> do-    (a, access) <- runSystemDyn g i wVar-    w <- readTVarIO wVar-    ((), w') <- runAccess access w-    atomically $ writeTVar wVar w'-    (b, access') <- runSystemDyn f a wVar-    return (b, access')--instance Arrow DynamicSystem where-  arr f = DynamicSystemT $ \i _ -> pure (f i, pure ())-  first s =-    DynamicSystemT $ \(i, x) w -> do-      (o, access) <- runSystemDyn s i w-      return ((o, x), access)--instance ArrowLoop DynamicSystem where-  loop s = DynamicSystemT $ \b w -> mdo-    ((c, d), access) <- runSystemDyn s (b, d) w-    return (c, access)---- | Combine two dynamic systems in parallel.-joinDyn :: DynamicSystem i a -> DynamicSystem i b -> DynamicSystem i (a, b)-joinDyn f g = DynamicSystemT $ \i w -> do-  fVar <- newTVarIO Nothing-  gVar <- newTVarIO Nothing-  _ <- forkIO $ do-    result <- runSystemDyn f i w-    atomically $ writeTVar fVar (Just result)-  _ <- forkIO $ do-    result <- runSystemDyn g i w-    atomically $ writeTVar gVar (Just result)-  let go = do-        maybeA <- readTVarIO fVar-        maybeB <- readTVarIO gVar-        case (maybeA, maybeB) of-          (Just (a, accessA), Just (b, accessB)) -> return ((a, b), accessA >> accessB)-          _ -> go-  go+instance (Monad m) => Category (DynamicSystemT m) where+  id = DynamicSystemT $ \_ -> \i -> return (i, mempty, pure ())+  DynamicSystemT f . DynamicSystemT g = DynamicSystemT $ \w -> \i -> do+    (b, gView, gAccess) <- g w i+    (a, fView, fAccess) <- f w b+    return (a, gView <> fView, gAccess >> fAccess) --- | Run a dynamic system forever.-foreverDyn :: DynamicSystem () () -> DynamicSystem () ()-foreverDyn s = DynamicSystemT $ \_ w -> do-  let go w' = do-        ((), access) <- runSystemDyn s () w'-        wAcc' <- readTVarIO w'-        ((), wAcc'') <- runAccess access wAcc'-        atomically $ writeTVar w' wAcc''-        go w'-  go w+instance (Monad m) => Arrow (DynamicSystemT m) where+  arr f = DynamicSystemT $ \_ -> \i -> return (f i, mempty, pure ())+  first (DynamicSystemT f) = DynamicSystemT $ \w -> \(i, x) -> do+    (a, v, access) <- f w i+    return ((a, x), v, access)  -- | Query all matching entities.-allDyn :: Set ComponentID -> DynamicQuery IO () a -> DynamicSystem () [a]-allDyn cIds q = DynamicSystemT $ \_ w -> do-  w' <- readTVarIO w-  let v = V.view cIds (archetypes w')-  fmap (\(a, _) -> (a, pure ())) (V.allDyn () q v)+allDyn :: (Monad m) => Set ComponentID -> DynamicQuery m i o -> DynamicSystemT m i [o]+allDyn cIds q = DynamicSystemT $ \w ->+  let !v = V.view cIds $ archetypes w+   in \i -> fmap (\(a, _) -> (a, mempty, pure ())) (V.allDyn i q v) -filterDyn :: Set ComponentID -> DynamicQuery IO i a -> (Node -> Bool) -> DynamicSystemT IO i [a]-filterDyn cIds q f = DynamicSystemT $ \i wVar -> do-  w <- readTVarIO wVar-  let v = V.filterView cIds f (archetypes w)-  (as, _) <- V.allDyn i q v-  return (as, pure ())+filterDyn :: (Monad m) => Set ComponentID -> DynamicQuery m i o -> (Node -> Bool) -> DynamicSystemT m i [o]+filterDyn cIds q f = DynamicSystemT $ \w ->+  let !v = V.filterView cIds f $ archetypes w+   in \i -> fmap (\(a, _) -> (a, mempty, pure ())) (V.allDyn i q v) --- | Query a single matching entity.--- If there are zero or multiple matching entities, an error will be thrown.-singleDyn :: Set ComponentID -> DynamicQuery IO () a -> DynamicSystem () a+singleDyn :: (Monad m) => Set ComponentID -> DynamicQuery m () a -> DynamicSystemT m () a singleDyn cIds q =   fmap     ( \as -> case as of@@ -279,30 +192,23 @@     (allDyn cIds q)  -- | Map all matching entities, storing the updated entities.-mapDyn :: Set ComponentID -> DynamicQuery IO i a -> DynamicSystem i [a]-mapDyn cIds q = DynamicSystemT $ \i wVar -> do-  w <- readTVarIO wVar-  let v = V.view cIds (archetypes w)-  (as, v') <- V.allDyn i q v-  atomically $ writeTVar wVar $ V.unview v' w-  return (as, pure ())--filterMapDyn :: Set ComponentID -> DynamicQuery IO i a -> (Node -> Bool) -> DynamicSystemT IO i [a]-filterMapDyn cIds q f = DynamicSystemT $ \i wVar -> do-  w <- readTVarIO wVar-  let v = V.filterView cIds f (archetypes w)-  (as, v') <- V.allDyn i q v-  atomically $ writeTVar wVar $ V.unview v' w-  return (as, pure ())+mapDyn :: (Monad m) => Set ComponentID -> DynamicQuery m i o -> DynamicSystemT m i [o]+mapDyn cIds q = DynamicSystemT $ \w ->+  let !v = V.view cIds $ archetypes w+   in \i -> fmap (\(a, v') -> (a, v', pure ())) (V.allDyn i q v) --- | Map all matching entities and ignore the output, storing the updated entities.-mapDyn_ :: Set ComponentID -> DynamicQuery IO () a -> DynamicSystem () ()-mapDyn_ cIds q = const () <$> mapDyn cIds q+filterMapDyn :: (Monad m) => Set ComponentID -> DynamicQuery m i o -> (Node -> Bool) -> DynamicSystemT m i [o]+filterMapDyn cIds q f = DynamicSystemT $ \w ->+  let !v = V.filterView cIds f $ archetypes w+   in \i -> fmap (\(a, v') -> (a, v', pure ())) (V.allDyn i q v) --- | Queue an `Access` to alter the world after this system is complete.-queueDyn :: (Applicative m) => (i -> Access m ()) -> DynamicSystemT m i ()-queueDyn f = DynamicSystemT $ \i _ -> pure ((), f i)+queueDyn :: (Monad m) => (i -> Access m ()) -> DynamicSystemT m i ()+queueDyn f = DynamicSystemT $ \_ -> \i -> return ((), mempty, f i) --- | Run a monadic task.-runDyn :: (Monad m) => (i -> m o) -> DynamicSystemT m i o-runDyn f = DynamicSystemT $ \i _ -> fmap (,pure ()) (f i)+raceDyn :: DynamicSystemT IO i a -> DynamicSystemT IO i b -> DynamicSystemT IO i (a, b)+raceDyn (DynamicSystemT f) (DynamicSystemT g) = DynamicSystemT $ \w -> \i -> do+  results <- parallel [fmap (\a -> (Just a, Nothing)) $ f w i, fmap (\b -> (Nothing, Just b)) $ g w i]+  ((a, v, fAccess), (b, v', gAccess)) <- case results of+    [(Just a, _), (_, Just b)] -> return (a, b)+    _ -> error "joinDyn: exception"+  return ((a, b), v <> v', fAccess >> gAccess)
src/Data/Aztecs/View.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE RankNTypes #-}@@ -20,11 +21,15 @@ import Data.Aztecs.World.Archetypes (ArchetypeID, Archetypes, Node (..)) import qualified Data.Aztecs.World.Archetypes as AS import Data.Aztecs.World.Components (ComponentID)-import Data.Foldable (foldrM)-import Data.Map (Map)-import qualified Data.Map as Map+import Data.Foldable (foldlM)+import Data.Map.Strict (Map)+import qualified Data.Map.Strict as Map import Data.Set (Set) +#if !MIN_VERSION_base(4,20,0)+import Data.Foldable (foldl')+#endif+ -- | View into a `World`, containing a subset of archetypes. newtype View = View {viewArchetypes :: Map ArchetypeID Node}   deriving (Show, Semigroup, Monoid)@@ -46,8 +51,8 @@ unview v w =   w     { W.archetypes =-        foldr-          (\(aId, n) as -> as {AS.nodes = Map.insert aId n (AS.nodes as)})+        foldl'+          (\as (aId, n) -> as {AS.nodes = Map.insert aId n (AS.nodes as)})           (W.archetypes w)           (Map.toList $ viewArchetypes v)     }@@ -56,8 +61,8 @@ allDyn :: (Monad m) => i -> DynamicQuery m i a -> View -> m ([a], View) allDyn i q v =   fmap (\(as, arches) -> (as, View arches)) $-    foldrM-      ( \(aId, n) (acc, archAcc) -> do+    foldlM+      ( \(acc, archAcc) (aId, n) -> do           (as, arch') <- dynQueryAll q (repeat i) (A.entities (nodeArchetype n)) (nodeArchetype n)           return (as ++ acc, Map.insert aId (n {nodeArchetype = arch'}) archAcc)       )
src/Data/Aztecs/World.hs view
@@ -1,8 +1,10 @@ {-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-}+{-# LANGUAGE BangPatterns #-}  module Data.Aztecs.World   ( World (..),@@ -38,12 +40,16 @@ import Data.Typeable (Proxy (..), Typeable, typeOf) import Prelude hiding (lookup) +#if !MIN_VERSION_base(4,20,0)+import Data.Foldable (foldl')+#endif+ -- | World of entities and their components. data World = World-  { archetypes :: Archetypes,-    components :: Components,-    entities :: Map EntityID ArchetypeID,-    nextEntityId :: EntityID+  { archetypes :: !Archetypes,+    components :: !Components,+    entities :: !(Map EntityID ArchetypeID),+    nextEntityId :: !EntityID   }   deriving (Show) @@ -115,11 +121,11 @@   World ->   (EntityID, World) spawnWithId cId c w =-  let (e, w') = spawnEmpty w+  let !(e, w') = spawnEmpty w    in case AS.lookupArchetypeId (Set.singleton cId) (archetypes w) of         Just aId -> (e, spawnWithArchetypeId' e aId cId c w')         Nothing ->-          let (aId, arches) =+          let !(aId, arches) =                 AS.insertArchetype                   (Set.singleton cId)                   ( Node@@ -142,7 +148,7 @@   World ->   (EntityID, World) spawnWithArchetypeId c cId aId w =-  let (e, w') = spawnEmpty w+  let !(e, w') = spawnEmpty w    in (e, spawnWithArchetypeId' e aId cId c w')  spawnWithArchetypeId' ::@@ -164,7 +170,7 @@ -- | Insert a component into an entity. insert :: forall a. (Component a, Typeable (StorageT a)) => EntityID -> a -> World -> World insert e c w =-  let (cId, components') = CS.insert @a (components w)+  let !(cId, components') = CS.insert @a (components w)    in insertWithId e cId c w {components = components'}  -- | Insert a component into an entity with its `ComponentID`.@@ -176,9 +182,9 @@         then w {archetypes = (archetypes w) {AS.nodes = Map.adjust (\n -> n {nodeArchetype = A.insertComponent e cId c (nodeArchetype n)}) aId (AS.nodes $ archetypes w)}}         else case AS.lookupArchetypeId (Set.insert cId (nodeComponentIds node)) (archetypes w) of           Just nextAId ->-            let (cs, arch') = A.remove e (nodeArchetype node)-                w' = w {archetypes = (archetypes w) {AS.nodes = Map.insert aId node {nodeArchetype = arch'} (AS.nodes $ archetypes w)}}-                f (itemCId, dyn) archAcc =+            let !(cs, arch') = A.remove e (nodeArchetype node)+                !w' = w {archetypes = (archetypes w) {AS.nodes = Map.insert aId node {nodeArchetype = arch'} (AS.nodes $ archetypes w)}}+                f archAcc (itemCId, dyn) =                   archAcc                     { A.storages =                         Map.adjust@@ -195,7 +201,7 @@                                   nextNode                                     { nodeArchetype =                                         A.insertComponent e cId c $-                                          foldr+                                          foldl'                                             f                                             (nodeArchetype nextNode)                                             (Map.toList cs)@@ -207,15 +213,15 @@                     entities = Map.insert e nextAId (entities w')                   }           Nothing ->-            let (s, arch') = A.removeStorages e (nodeArchetype node)-                n =+            let !(s, arch') = A.removeStorages e (nodeArchetype node)+                !n =                   Node                     { nodeComponentIds = Set.insert cId (nodeComponentIds node),                       nodeArchetype = A.insertComponent e cId c (Archetype {A.storages = s}),                       nodeAdd = Map.empty,                       nodeRemove = Map.singleton cId aId                     }-                (nextAId, arches) = AS.insertArchetype (Set.insert cId (nodeComponentIds node)) n (archetypes w)+                !(nextAId, arches) = AS.insertArchetype (Set.insert cId (nodeComponentIds node)) n (archetypes w)              in w                   { archetypes =                       arches@@ -249,21 +255,21 @@  lookup :: forall a. (Component a) => EntityID -> World -> Maybe a lookup e w = do-  cId <- CS.lookup @a (components w)-  aId <- Map.lookup e (entities w)-  node <- AS.lookupNode aId (archetypes w)+  !cId <- CS.lookup @a (components w)+  !aId <- Map.lookup e (entities w)+  !node <- AS.lookupNode aId (archetypes w)   A.lookupComponent e cId (nodeArchetype node)  -- | Despawn an entity, returning its components. despawn :: EntityID -> World -> (Map ComponentID Dynamic, World) despawn e w =   let res = do-        aId <- Map.lookup e (entities w)-        node <- AS.lookupNode aId (archetypes w)+        !aId <- Map.lookup e (entities w)+        !node <- AS.lookupNode aId (archetypes w)         return (aId, node)    in case res of         Just (aId, node) ->-          let (dynAcc, arch') = A.remove e (nodeArchetype node)+          let !(dynAcc, arch') = A.remove e (nodeArchetype node)            in ( dynAcc,                 w                   { archetypes = (archetypes w) {AS.nodes = Map.insert aId node {nodeArchetype = arch'} (AS.nodes $ archetypes w)},
src/Data/Aztecs/World/Archetype.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}@@ -39,19 +41,23 @@ import qualified Data.Aztecs.World.Components as CS import Data.Bifunctor (Bifunctor (..)) import Data.Dynamic (Dynamic, Typeable, fromDynamic, toDyn)-import Data.Map (Map)-import qualified Data.Map as Map+import Data.Map.Strict (Map)+import qualified Data.Map.Strict as Map import Data.Maybe (fromMaybe) import Data.Set (Set) import qualified Data.Set as Set import Prelude hiding (all, lookup) +#if !MIN_VERSION_base(4,20,0)+import Data.Foldable (foldl')+#endif+ data AnyStorage = AnyStorage-  { storageDyn :: Dynamic,-    insertDyn :: Int -> Dynamic -> Dynamic -> Dynamic,-    removeDyn :: Int -> Dynamic -> (Maybe Dynamic, Dynamic),-    removeAny :: Int -> Dynamic -> (Maybe AnyStorage, Dynamic),-    entitiesDyn :: Dynamic -> [Int]+  { storageDyn :: !Dynamic,+    insertDyn :: !(Int -> Dynamic -> Dynamic -> Dynamic),+    removeDyn :: !(Int -> Dynamic -> (Maybe Dynamic, Dynamic)),+    removeAny :: !(Int -> Dynamic -> (Maybe AnyStorage, Dynamic)),+    entitiesDyn :: !(Dynamic -> [Int])   }  instance Show AnyStorage where@@ -63,14 +69,14 @@     { storageDyn = toDyn s,       insertDyn = \i cDyn sDyn ->         fromMaybe sDyn $ do-          s' <- fromDynamic @(s a) sDyn-          c <- fromDynamic cDyn+          !s' <- fromDynamic @(s a) sDyn+          !c <- fromDynamic cDyn           return . toDyn $ S.insert i c s',       removeDyn = \i dyn -> case fromDynamic @(s a) dyn of-        Just s' -> let (a, b) = S.remove i s' in (fmap toDyn a, toDyn b)+        Just s' -> let !(a, b) = S.remove i s' in (fmap toDyn a, toDyn b)         Nothing -> (Nothing, dyn),       removeAny = \i dyn -> case fromDynamic @(s a) dyn of-        Just s' -> let (a, b) = S.remove i s' in (fmap (anyStorage . S.singleton @s i) a, toDyn b)+        Just s' -> let !(a, b) = S.remove i s' in (fmap (anyStorage . S.singleton @s i) a, toDyn b)         Nothing -> (Nothing, dyn),       entitiesDyn = \dyn -> case fromDynamic @(s a) dyn of         Just s' -> map fst $ S.all s'@@ -90,7 +96,7 @@  insertComponent :: forall a. (Component a) => EntityID -> ComponentID -> a -> Archetype -> Archetype insertComponent e cId c arch =-  let storage = case lookupStorage cId arch of+  let !storage = case lookupStorage cId arch of         Just s -> S.insert (unEntityId e) c s         Nothing -> S.singleton @(StorageT a) @a (unEntityId e) c    in arch {storages = Map.insert cId (anyStorage storage) (storages arch)}@@ -120,32 +126,30 @@  insertAscList :: forall a. (Component a) => ComponentID -> [(EntityID, a)] -> Archetype -> Archetype insertAscList cId as arch =-  arch-    { storages =+  let !storages' =         Map.insert           cId           (anyStorage $ S.fromAscList @(StorageT a) (map (first unEntityId) as))           (storages arch)-    }+   in arch {storages = storages'}  withAscList :: forall a. (Component a) => ComponentID -> [a] -> Archetype -> Archetype withAscList cId as arch =-  arch-    { storages =+  let !storages' =         Map.adjust           ( \s ->               (anyStorage $ S.fromAscList @(StorageT a) (zip (entitiesDyn s (storageDyn s)) as))           )           cId           (storages arch)-    }+   in arch {storages = storages'}  remove :: EntityID -> Archetype -> (Map ComponentID Dynamic, Archetype) remove e arch =-  foldr-    ( \(cId, s) (dynAcc, archAcc) ->-        let (dynA, dynS) = removeDyn s (unEntityId e) (storageDyn s)-            dynAcc' = case dynA of+  foldl'+    ( \(dynAcc, archAcc) (cId, s) ->+        let !(dynA, dynS) = removeDyn s (unEntityId e) (storageDyn s)+            !dynAcc' = case dynA of               Just d -> Map.insert cId d dynAcc               Nothing -> dynAcc          in ( dynAcc',@@ -157,8 +161,8 @@  removeStorages :: EntityID -> Archetype -> (Map ComponentID AnyStorage, Archetype) removeStorages e arch =-  foldr-    ( \(cId, s) (dynAcc, archAcc) ->+  foldl'+    ( \(dynAcc, archAcc) (cId, s) ->         let (dynA, dynS) = removeAny s (unEntityId e) (storageDyn s)             dynAcc' = case dynA of               Just d -> Map.insert cId d dynAcc@@ -198,6 +202,6 @@  runBundle :: Bundle -> Components -> EntityID -> Archetype -> (Components, Archetype) runBundle b cs eId arch =-  let (_, cs', d) = unBundle b cs-      arch' = runDynamicBundle d eId arch+  let !(_, cs', d) = unBundle b cs+      !arch' = runDynamicBundle d eId arch    in (cs', arch')
src/Data/Aztecs/World/Archetypes.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-}@@ -22,13 +24,17 @@  import Data.Aztecs.Component (ComponentID) import Data.Aztecs.World.Archetype hiding (empty)-import Data.Map (Map)-import qualified Data.Map as Map+import Data.Map.Strict (Map)+import qualified Data.Map.Strict as Map import Data.Maybe (mapMaybe) import Data.Set (Set) import qualified Data.Set as Set import Prelude hiding (all, lookup, map) +#if !MIN_VERSION_base(4,20,0)+import Data.Foldable (foldl')+#endif+ -- | `Archetype` ID. newtype ArchetypeID = ArchetypeID {unArchetypeId :: Int}   deriving (Eq, Ord, Show)@@ -36,26 +42,26 @@ -- | Node in `Archetypes`. data Node = Node   { -- | Unique set of `ComponentID`s of this `Node`.-    nodeComponentIds :: Set ComponentID,+    nodeComponentIds :: !(Set ComponentID),     -- | `Archetype` of this `Node`.-    nodeArchetype :: Archetype,+    nodeArchetype :: !Archetype,     -- | Edges to other `Archetype`s by adding a `ComponentID`.-    nodeAdd :: Map ComponentID ArchetypeID,+    nodeAdd :: !(Map ComponentID ArchetypeID),     -- | Edges to other `Archetype`s by removing a `ComponentID`.-    nodeRemove :: Map ComponentID ArchetypeID+    nodeRemove :: !(Map ComponentID ArchetypeID)   }   deriving (Show)  -- | `Archetype` graph. data Archetypes = Archetypes   { -- | Archetype nodes in the graph.-    nodes :: Map ArchetypeID Node,+    nodes :: !(Map ArchetypeID Node),     -- | Mapping of unique `ComponentID` sets to `ArchetypeID`s.-    archetypeIds :: Map (Set ComponentID) ArchetypeID,+    archetypeIds :: !(Map (Set ComponentID) ArchetypeID),     -- | Next unique `ArchetypeID`.-    nextArchetypeId :: ArchetypeID,+    nextArchetypeId :: !ArchetypeID,     -- | Mapping of `ComponentID`s to `ArchetypeID`s of `Archetypes` that contain them.-    componentIds :: Map ComponentID (Set ArchetypeID)+    componentIds :: !(Map ComponentID (Set ArchetypeID))   }   deriving (Show) @@ -88,7 +94,7 @@ -- | Find `ArchetypeID`s containing a set of `ComponentID`s. findArchetypeIds :: Set ComponentID -> Archetypes -> Set ArchetypeID findArchetypeIds cIds arches = case mapMaybe (\cId -> Map.lookup cId (componentIds arches)) (Set.elems cIds) of-  (aId : aIds') -> foldr Set.intersection aId aIds'+  (aId : aIds') -> foldl' Set.intersection aId aIds'   [] -> Set.empty  -- | Lookup `Archetype`s containing a set of `ComponentID`s.@@ -101,10 +107,10 @@ -- | Map over `Archetype`s containing a set of `ComponentID`s. map :: Set ComponentID -> (Archetype -> (a, Archetype)) -> Archetypes -> ([a], Archetypes) map cIds f arches =-  foldr-    ( \aId (acc, archAcc) ->-        let node = nodes archAcc Map.! aId-            (a, arch') = f (nodeArchetype node)+  foldl'+    ( \(acc, archAcc) aId ->+        let !node = nodes archAcc Map.! aId+            !(a, arch') = f (nodeArchetype node)          in (a : acc, archAcc {nodes = Map.insert aId (node {nodeArchetype = arch'}) (nodes archAcc)})     )     ([], arches)
src/Data/Aztecs/World/Components.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE BangPatterns #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} @@ -13,15 +14,15 @@ where  import Data.Aztecs.Component (Component, ComponentID (..))-import Data.Map (Map)-import qualified Data.Map as Map+import Data.Map.Strict (Map)+import qualified Data.Map.Strict as Map import Data.Typeable (Proxy (..), TypeRep, Typeable, typeOf) import Prelude hiding (lookup)  -- | Component ID map. data Components = Components-  { componentIds :: Map TypeRep ComponentID,-    nextComponentId :: ComponentID+  { componentIds :: !(Map TypeRep ComponentID),+    nextComponentId :: !ComponentID   }   deriving (Show) @@ -46,7 +47,7 @@ -- | Insert a component ID by type. insert' :: forall c. (Component c) => Components -> (ComponentID, Components) insert' cs =-  let cId = nextComponentId cs+  let !cId = nextComponentId cs    in ( cId,         cs           { componentIds = Map.insert (typeOf (Proxy @c)) cId (componentIds cs),