packages feed

apecs 0.2.2.0 → 0.2.3.0

raw patch · 11 files changed

+313/−218 lines, 11 filesdep +template-haskellPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: template-haskell

API changes (from Hackage documentation)

- Apecs: initCounter :: IO (Storage EntityCounter)
- Apecs: initStoreWith :: Store s => InitArgs s -> IO s
- Apecs.Stores: instance Apecs.Types.GlobalStore (Apecs.Stores.Const c)
- Apecs.Stores: instance Apecs.Types.GlobalStore (Apecs.Stores.Global c)
- Apecs.Stores: instance Apecs.Types.Store (Apecs.Stores.Const c)
- Apecs.Stores: instance Apecs.Types.Store (Apecs.Stores.Global c)
- Apecs.Types: initStoreWith :: Store s => InitArgs s -> IO s
- Apecs.Types: instance (Apecs.Types.Component a, Apecs.Types.Component b) => Apecs.Types.Component (a, b)
- Apecs.Types: instance (Apecs.Types.Component a, Apecs.Types.Component b, Apecs.Types.Component c) => Apecs.Types.Component (a, b, c)
- Apecs.Types: instance (Apecs.Types.Has w a, Apecs.Types.Has w b) => Apecs.Types.Has w (a, b)
- Apecs.Types: instance (Apecs.Types.Has w a, Apecs.Types.Has w b, Apecs.Types.Has w c) => Apecs.Types.Has w (a, b, c)
- Apecs.Types: instance (Apecs.Types.Store a, Apecs.Types.Store b) => Apecs.Types.Store (a, b)
- Apecs.Types: instance (Apecs.Types.Store a, Apecs.Types.Store b, Apecs.Types.Store c) => Apecs.Types.Store (a, b, c)
- Apecs.Util: initCounter :: IO (Storage EntityCounter)
+ Apecs.Stores: instance GHC.Base.Monoid c => Apecs.Types.GlobalStore (Apecs.Stores.Const c)
+ Apecs.Stores: instance GHC.Base.Monoid c => Apecs.Types.GlobalStore (Apecs.Stores.Global c)
+ Apecs.Stores: instance GHC.Base.Monoid c => Apecs.Types.Store (Apecs.Stores.Const c)
+ Apecs.Stores: instance GHC.Base.Monoid c => Apecs.Types.Store (Apecs.Stores.Global c)
+ Apecs.TH: makeWorld :: String -> [Name] -> Q [Dec]
+ Apecs.TH: makeWorldNoEC :: String -> [Name] -> Q [Dec]
+ Apecs.Types: initStore :: Store s => IO s
+ Apecs.Types: instance (Apecs.Types.Component t_0, Apecs.Types.Component t_1) => Apecs.Types.Component (t_0, t_1)
+ Apecs.Types: instance (Apecs.Types.Component t_0, Apecs.Types.Component t_1, Apecs.Types.Component t_2) => Apecs.Types.Component (t_0, t_1, t_2)
+ Apecs.Types: instance (Apecs.Types.Component t_0, Apecs.Types.Component t_1, Apecs.Types.Component t_2, Apecs.Types.Component t_3) => Apecs.Types.Component (t_0, t_1, t_2, t_3)
+ Apecs.Types: instance (Apecs.Types.Component t_0, Apecs.Types.Component t_1, Apecs.Types.Component t_2, Apecs.Types.Component t_3, Apecs.Types.Component t_4) => Apecs.Types.Component (t_0, t_1, t_2, t_3, t_4)
+ Apecs.Types: instance (Apecs.Types.Component t_0, Apecs.Types.Component t_1, Apecs.Types.Component t_2, Apecs.Types.Component t_3, Apecs.Types.Component t_4, Apecs.Types.Component t_5) => Apecs.Types.Component (t_0, t_1, t_2, t_3, t_4, t_5)
+ Apecs.Types: instance (Apecs.Types.Has w t_0, Apecs.Types.Has w t_1) => Apecs.Types.Has w (t_0, t_1)
+ Apecs.Types: instance (Apecs.Types.Has w t_0, Apecs.Types.Has w t_1, Apecs.Types.Has w t_2) => Apecs.Types.Has w (t_0, t_1, t_2)
+ Apecs.Types: instance (Apecs.Types.Has w t_0, Apecs.Types.Has w t_1, Apecs.Types.Has w t_2, Apecs.Types.Has w t_3) => Apecs.Types.Has w (t_0, t_1, t_2, t_3)
+ Apecs.Types: instance (Apecs.Types.Has w t_0, Apecs.Types.Has w t_1, Apecs.Types.Has w t_2, Apecs.Types.Has w t_3, Apecs.Types.Has w t_4) => Apecs.Types.Has w (t_0, t_1, t_2, t_3, t_4)
+ Apecs.Types: instance (Apecs.Types.Has w t_0, Apecs.Types.Has w t_1, Apecs.Types.Has w t_2, Apecs.Types.Has w t_3, Apecs.Types.Has w t_4, Apecs.Types.Has w t_5) => Apecs.Types.Has w (t_0, t_1, t_2, t_3, t_4, t_5)
+ Apecs.Types: instance (Apecs.Types.Store t_0, Apecs.Types.Store t_1) => Apecs.Types.Store (t_0, t_1)
+ Apecs.Types: instance (Apecs.Types.Store t_0, Apecs.Types.Store t_1, Apecs.Types.Store t_2) => Apecs.Types.Store (t_0, t_1, t_2)
+ Apecs.Types: instance (Apecs.Types.Store t_0, Apecs.Types.Store t_1, Apecs.Types.Store t_2, Apecs.Types.Store t_3) => Apecs.Types.Store (t_0, t_1, t_2, t_3)
+ Apecs.Types: instance (Apecs.Types.Store t_0, Apecs.Types.Store t_1, Apecs.Types.Store t_2, Apecs.Types.Store t_3, Apecs.Types.Store t_4) => Apecs.Types.Store (t_0, t_1, t_2, t_3, t_4)
+ Apecs.Types: instance (Apecs.Types.Store t_0, Apecs.Types.Store t_1, Apecs.Types.Store t_2, Apecs.Types.Store t_3, Apecs.Types.Store t_4, Apecs.Types.Store t_5) => Apecs.Types.Store (t_0, t_1, t_2, t_3, t_4, t_5)
+ Apecs.Util: instance GHC.Base.Monoid Apecs.Util.EntityCounter
- Apecs: initStore :: (Store s, InitArgs s ~ ()) => IO s
+ Apecs: initStore :: Store s => IO s
- Apecs.Types: class Store s where type Stores s type SafeRW s type InitArgs s explReset s = do { sl <- explMembers s; mapM_ (explDestroy s) sl } explImapM_ s ma = liftIO (explMembers s) >>= mapM_ ma . toList explImapM s ma = liftIO (explMembers s) >>= mapM ma . toList explModify s ety f = do { etyExists <- explExists s ety; when etyExists $ explGetUnsafe s ety >>= explSet s ety . f } explCmap s f = explMembers s >>= mapM_ (\ ety -> explModify s ety f) explCmapM_ s sys = do { sl <- liftIO $ explMembers s; forM_ sl $ \ ety -> do { x :: Stores s <- liftIO $ explGetUnsafe s ety; sys x } } explCimapM_ s sys = do { sl <- liftIO $ explMembers s; forM_ sl $ \ ety -> do { x :: Stores s <- liftIO $ explGetUnsafe s ety; sys (ety, x) } } explCmapM s sys = do { sl <- liftIO $ explMembers s; for (toList sl) $ \ ety -> do { x :: Stores s <- liftIO $ explGetUnsafe s ety; sys x } } explCimapM s sys = do { sl <- liftIO $ explMembers s; for (toList sl) $ \ ety -> do { x :: Stores s <- liftIO $ explGetUnsafe s ety; sys (ety, x) } } where {
+ Apecs.Types: class Store s where type Stores s type SafeRW s explReset s = do { sl <- explMembers s; mapM_ (explDestroy s) sl } explImapM_ s ma = liftIO (explMembers s) >>= mapM_ ma . toList explImapM s ma = liftIO (explMembers s) >>= mapM ma . toList explModify s ety f = do { etyExists <- explExists s ety; when etyExists $ explGetUnsafe s ety >>= explSet s ety . f } explCmap s f = explMembers s >>= mapM_ (\ ety -> explModify s ety f) explCmapM_ s sys = do { sl <- liftIO $ explMembers s; forM_ sl $ \ ety -> do { x :: Stores s <- liftIO $ explGetUnsafe s ety; sys x } } explCimapM_ s sys = do { sl <- liftIO $ explMembers s; forM_ sl $ \ ety -> do { x :: Stores s <- liftIO $ explGetUnsafe s ety; sys (ety, x) } } explCmapM s sys = do { sl <- liftIO $ explMembers s; for (toList sl) $ \ ety -> do { x :: Stores s <- liftIO $ explGetUnsafe s ety; sys x } } explCimapM s sys = do { sl <- liftIO $ explMembers s; for (toList sl) $ \ ety -> do { x :: Stores s <- liftIO $ explGetUnsafe s ety; sys (ety, x) } } where {
- Apecs.Types: type family InitArgs s;
+ Apecs.Types: type family SafeRW s;
- Apecs.Util: initStore :: (Store s, InitArgs s ~ ()) => IO s
+ Apecs.Util: initStore :: Store s => IO s

Files

README.md view
@@ -1,5 +1,5 @@ # apecs-##### [hackage](https://hackage.haskell.org/package/apecs) | [documentation](https://hackage.haskell.org/package/apecs-0.2.0.2/docs/Apecs.html) | [tutorials](https://github.com/jonascarpay/apecs/blob/master/tutorials/)+##### [hackage](https://hackage.haskell.org/package/apecs) | [documentation](https://hackage.haskell.org/package/apecs/docs/Apecs.html) | [tutorials](https://github.com/jonascarpay/apecs/blob/master/tutorials/)  apecs is an Entity Component System inspired by [specs](https://github.com/slide-rs/specs) and [Entitas](https://github.com/sschmid/Entitas-CSharp). It exposes a DSL that translates to fast storage operations, resulting in expressivity without sacrificing performance or safety.@@ -19,64 +19,48 @@ ### Example ```haskell import Apecs+import Apecs.TH (makeWorld) import Apecs.Stores (Cache) import Linear --- Component data definitions-newtype Velocity = Velocity (V2 Double) deriving (Eq, Show)-newtype Position = Position (V2 Double) deriving (Eq, Show)-data Enemy = Enemy -- A single constructor for tagging entites as enemies+newtype Position = Position (V2 Double) deriving Show+-- Turn Position into a component by specifiying the type of its Storage+instance Component Position where+  -- The simplest store is a Map+  type Storage Position = Map Position --- Define Velocity as a component by giving it a storage type+newtype Velocity = Velocity (V2 Double) instance Component Velocity where-  -- Store velocities in a cached map+  -- We can add a Cache for faster access   type Storage Velocity = Cache 100 (Map Velocity) -instance Component Position where-  type Storage Position = Cache 100 (Map Position)--instance Flag Enemy where flag = Enemy-instance Component Enemy where-  -- Because enemy is just a flag, we can use a set-  type Storage Enemy = Set Enemy---- Define your world as containing the storages of your components-data World = World-  { positions     :: Storage Position-  , velocities    :: Storage Velocity-  , enemies       :: Storage Enemy-  , entityCounter :: Storage EntityCounter }---- Define Has instances for components to allow type-driven access to their storages-instance World `Has` Position      where getStore = System $ asks positions-instance World `Has` Velocity      where getStore = System $ asks velocities-instance World `Has` Enemy         where getStore = System $ asks enemies-instance World `Has` EntityCounter where getStore = System $ asks entityCounter+data Player = Player -- A single constructor component for tagging the player+instance Component Player where+  -- Unique contains at most one component. See the Stores module.+  type Storage Player = Unique Player -type System' a = System World a+-- Generate a world type and instances+makeWorld "World" [''Position, ''Velocity, ''Player] -game :: System' ()+game :: System World () game = do   -- Create new entities   ety <- newEntity (Position 0)   -- Components can be composed using tuples   newEntity (Position 0, Velocity 1)-  -- Tagging one as an enemy is a matter of adding the constructor-  newEntity (Position 1, Velocity 1, Enemy)+  newEntity (Position 1, Velocity 1, Player) +  -- set (over)writes components+  set ety (Velocity 2)+   -- Side effects   liftIO$ putStrLn "Stepping velocities"   -- rmap maps a pure function over all entities in its domain   rmap $ \(Position p, Velocity v) -> Position (v+p) -  -- Set can be used to (over)write components-  set ety (Position 2, Enemy)--  -- Print the positions of all enemies-  cmapM_ $ \(Enemy, Position p) -> liftIO (print p)+  -- Print all positions+  cmapM_ $ \(Position p) -> liftIO (print p)  main :: IO ()-main = do w <- World <$> initStore <*> initStore <*> initStore <*> initCounter-          runSystem game w-+main = initWorld >>= runSystem game ```
apecs.cabal view
@@ -1,5 +1,5 @@ name:                apecs-version:             0.2.2.0+version:             0.2.3.0 homepage:            https://github.com/jonascarpay/apecs#readme license:             BSD3 license-file:        LICENSE@@ -22,18 +22,21 @@     Apecs.Logs,     Apecs.System,     Apecs.Slice,+    Apecs.TH,     Apecs.Util+  other-modules:+    Apecs.THTuples   default-language:     Haskell2010   build-depends:     base >= 4.7 && < 5,     containers,     mtl,+    template-haskell,     vector   ghc-options:     -Wall     -Odph-    -fno-warn-unused-top-binds  test-suite apecs-test   type:
bench/Main.hs view
@@ -1,61 +1,18 @@ {-# LANGUAGE Strict, ScopedTypeVariables, DataKinds, TypeFamilies, MultiParamTypeClasses, TypeOperators #-} {-# LANGUAGE FlexibleContexts, FlexibleInstances #-}+{-# LANGUAGE TemplateHaskell #-}  import Criterion import qualified Criterion.Main as C import Control.Monad+import Linear -import Apecs as A+import Apecs+import Apecs.TH import Apecs.Stores import Apecs.Util import qualified Apecs.Slice as S -import Linear--data Group w1 w2 = Group-  { groupName :: String-  , naiveWorld :: IO w1-  , naiveInit :: System w1 ()-  , naiveRun  :: System w1 ()-  , improvedWorld :: IO w1-  , improvedInit :: System w1 ()-  , improvedRun  :: System w1 () }--toBench (Group name w1 i1 r1 w2 i2 r2) =-  bgroup name-    [ bgroup "naive"    [bench "init" $ whnfIO (w1 >>= runSystem i1), bench "init and run" $ whnfIO (w2 >>= runSystem (i1 >> r1))]-    , bgroup "improved" [bench "init" $ whnfIO (w1 >>= runSystem i1), bench "init and run" $ whnfIO (w2 >>= runSystem (i2 >> r2))]-    ]--data W1 c = W1 {w1c1 :: (Storage c), w1ec :: Storage EntityCounter}-instance Component c => Has (W1 c) c where getStore = System $ asks w1c1-instance Has (W1 c) EntityCounter where getStore = System $ asks w1ec--w1with args = W1 <$> initStoreWith args <*> initCounter-w1 = w1with ()-w2with a1 a2 = W2 <$> initStoreWith a1 <*> initStoreWith a2 <*> initCounter-w2 = w2with () ()--data W2 a b = W2 { w2c1 :: Storage a , w2c2 :: Storage b, w2ec :: Storage EntityCounter}-instance (Component a, Component b) => Has (W2 a b) a where getStore = System $ asks w2c1-instance (Component a, Component b) => Has (W2 a b) b where getStore = System $ asks w2c2-instance Has (W2 a b) EntityCounter where getStore = System $ asks w2ec----Explicit vs implicit map-newtype Counter = Counter Int-instance Component Counter where type Storage Counter = Map Counter--mapExample = Group-  { groupName     = "Single component map"-  , naiveWorld    = w1 :: IO (W1 Counter)-  , naiveInit     = replicateM_ 10 (newEntity (Counter 0))-  , naiveRun      = owners >>= S.mapM_ (\(e :: Entity Counter) -> set e (Counter 1))-  , improvedWorld = w1 :: IO (W1 Counter)-  , improvedInit  = replicateM_ 10 (newEntity (Counter 0))-  , improvedRun   = cmap (const (Counter 1))-  }-- -- ecs_bench newtype ECSPos = ECSPos (V2 Float) deriving (Eq, Show) instance Component ECSPos where type Storage ECSPos = Cache 10000 (Map ECSPos)@@ -63,19 +20,17 @@ newtype ECSVel = ECSVel (V2 Float) deriving (Eq, Show) instance Component ECSVel where type Storage ECSVel = Cache 1000 (Map ECSVel) +makeWorld "ECSB" [''ECSPos, ''ECSVel]+ pvInit = do replicateM_ 1000 (newEntity (ECSPos 0, ECSVel 1))             replicateM_ 9000 (newEntity (ECSPos 0))  pvStep = rmap $ \(ECSVel v, ECSPos p) -> ECSPos (p+v) -pvWorld :: IO (W2 ECSPos ECSVel)-pvWorld = w2- main :: IO () main = C.defaultMain   [ bgroup "ecs_bench"-    [ bench "init" $ whnfIO (pvWorld >>= runSystem pvInit)-    , bench "step" $ whnfIO (pvWorld >>= runSystem (pvInit >> pvStep))+    [ bench "init" $ whnfIO (initECSB >>= runSystem pvInit)+    , bench "step" $ whnfIO (initECSB >>= runSystem (pvInit >> pvStep))     ]-  , toBench mapExample   ]
src/Apecs.hs view
@@ -9,13 +9,9 @@     Map, Set, Unique, Global, Flag(..),  -  -- * Initializable-    initStoreWith,--  -- ** HasMembers wrapper functions+  -- * Store wrapper functions+    initStore,     destroy, exists, owners, resetStore,--  -- ** Store wrapper functions     get, set, set', modify,     cmap, cmapM, cmapM_, cimapM, cimapM_,     rmap', rmap, wmap, wmap', cmap',@@ -26,7 +22,7 @@    -- * Other     runSystem, runWith,-    initStore, runGC, EntityCounter, initCounter, newEntity,+    runGC, EntityCounter, newEntity,    -- Reader   asks, ask, liftIO, lift,
src/Apecs/Logs.hs view
@@ -66,9 +66,8 @@ data Logger l s = Logger (l (Stores s)) s  instance (Log l (Stores s), Cachable s) => Store (Logger l s) where-  type InitArgs (Logger l s) = InitArgs s   type Stores (Logger l s) = Stores s-  initStoreWith args = Logger <$> logEmpty <*> initStoreWith args+  initStore = Logger <$> logEmpty <*> initStore    {-# INLINE explDestroy #-}   explDestroy (Logger l s) ety = do
src/Apecs/Stores.hs view
@@ -30,13 +30,12 @@ defaultSetMaybe s e Nothing  = explDestroy s e defaultSetMaybe s e (Just c) = explSet s e c --- | A map from Data.Intmap.Strict. O(n log(n)) for most operations.+-- | A map from Data.Intmap.Strict. O(log(n)) for most operations. --   Yields safe runtime representations of type @Maybe c@. newtype Map c = Map (IORef (M.IntMap c)) instance Store (Map c) where-  type InitArgs (Map c) = ()   type Stores (Map c) = c-  initStoreWith _ = Map <$> newIORef mempty+  initStore = Map <$> newIORef mempty   explDestroy (Map ref) ety = modifyIORef' ref (M.delete ety)   explMembers (Map ref)     = U.fromList . M.keys <$> readIORef ref   explExists  (Map ref) ety = M.member ety <$> readIORef ref@@ -75,9 +74,8 @@ --   Produces @flag@ runtime values. newtype Set c = Set (IORef S.IntSet) instance Flag c => Store (Set c) where-  type InitArgs (Set c) = ()   type Stores (Set c) = c-  initStoreWith _ = Set <$> newIORef mempty+  initStore = Set <$> newIORef mempty   explDestroy (Set ref) ety = modifyIORef' ref (S.delete ety)   explMembers (Set ref) = U.fromList . S.toList <$> readIORef ref   explReset (Set ref) = writeIORef ref mempty@@ -113,9 +111,8 @@ --   Writing to it overwrites both the previous component and its owner. data Unique c = Unique (IORef Int) (IORef c) instance Store (Unique c) where-  type InitArgs (Unique c) = ()   type Stores (Unique c) = c-  initStoreWith _ = Unique <$> newIORef (-1) <*> newIORef undefined+  initStore = Unique <$> newIORef (-1) <*> newIORef undefined   explDestroy (Unique eref _) ety = do e <- readIORef eref; when (e==ety) (writeIORef eref (-1))   explMembers (Unique eref _) = U.singleton <$> readIORef eref   explReset   (Unique eref _) = writeIORef eref (-1)@@ -171,11 +168,11 @@   -- | Constant value. Not very practical, but fun to write.+--   Contains `mempty` newtype Const c = Const c-instance Store (Const c) where-  type InitArgs (Const c) = c+instance Monoid c => Store (Const c) where   type Stores (Const c) = c-  initStoreWith c = return$ Const c+  initStore = return$ Const mempty   explDestroy _ _ = return ()   explExists  _ _  = return False   explMembers _ = return mempty@@ -187,16 +184,15 @@   explSetMaybe  _ _ _ = return ()   explModify    _ _ _ = return ()   explCmap       _ _ = return ()-instance GlobalStore (Const c) where+instance Monoid c => GlobalStore (Const c) where  -- | Global value.---   Must be given an initial value upon construction.+--   Initialized with 'mempty' newtype Global c = Global (IORef c)-instance GlobalStore (Global c) where-instance Store (Global c) where-  type InitArgs (Global c) = c+instance Monoid c => GlobalStore (Global c) where+instance Monoid c => Store (Global c) where   type Stores   (Global c) = c-  initStoreWith c = Global <$> newIORef c+  initStore = Global <$> newIORef mempty    type SafeRW (Global c) = c   explDestroy _ _ = return ()@@ -219,13 +215,12 @@ instance (KnownNat n, Cachable s) => Cachable (Cache n s)  instance (KnownNat n, Cachable s) => Store (Cache n s) where-  type InitArgs (Cache n s) = (InitArgs s)   type Stores (Cache n s) = Stores s-  initStoreWith args = do+  initStore = do     let n = fromIntegral$ natVal (Proxy @n)     tags <- UM.replicate n (-1)     cache <- VM.new n-    child <- initStoreWith args+    child <- initStore     return (Cache n tags cache child)    {-# INLINE explDestroy #-}
+ src/Apecs/TH.hs view
@@ -0,0 +1,61 @@+{-# LANGUAGE TemplateHaskell #-}++module Apecs.TH+  ( makeWorld, makeWorldNoEC+  )where++import Language.Haskell.TH++import Apecs.Util (EntityCounter)++genName :: String -> Q Name+genName s = mkName . show <$> newName s++-- | Same as 'makeWorld', but has no 'EntityCounter'+makeWorldNoEC :: String -> [Name] -> Q [Dec]+makeWorldNoEC worldName cTypes = do+  cTypesNames <- mapM (\t -> do rec <- genName "rec"; return (ConT t, rec)) cTypes++  let wld = mkName worldName+      has = mkName "Has"+      sys = mkName "System"+      wldDecl = DataD [] wld [] Nothing [RecC wld records] []++      makeRecord (t,n) = (n, Bang NoSourceUnpackedness SourceStrict, ConT (mkName "Storage") `AppT` t)+      records = makeRecord <$> cTypesNames++      makeInstance (t,n) =+        InstanceD Nothing [] ((ConT has `AppT` ConT wld) `AppT` t)+          [ FunD (mkName "getStore") [Clause []+              (NormalB$ ConE sys `AppE` (VarE (mkName "asks") `AppE` VarE n))+            [] ]+          ]++      initDecl = FunD (mkName $ "init" ++ worldName) [Clause []+        (NormalB$ iterate (\wE -> AppE (AppE (VarE $ mkName "<*>") wE) (VarE $ mkName "initStore")) (AppE (VarE $ mkName "return") (ConE wld)) !! length records)+        [] ]++      hasDecl = makeInstance <$> cTypesNames++  return $ wldDecl : initDecl : hasDecl++{-|++> makeWorld "WorldName" [''Component1, ''Component2, ...]++turns into++> data WorldName = WorldName ...+> instance WorldName `Has` Component1 where ...+> instance WorldName `Has` Component2 where ...+> ...+>+> instance WorldName `Has` EntityCounter where ...+>+> initWorldName :: IO WorldName+> initWorldName = WorldName <$> initStore <*> ...++|-}+makeWorld :: String -> [Name] -> Q [Dec]+makeWorld worldName cTypes = makeWorldNoEC worldName (cTypes ++ [''EntityCounter])+
+ src/Apecs/THTuples.hs view
@@ -0,0 +1,170 @@+{-# LANGUAGE TemplateHaskell #-}++module Apecs.THTuples where++import Language.Haskell.TH+import qualified Data.Vector.Unboxed as U++makeInstances is = concat <$> traverse tupleInstances is++{--+instance (Component a, Component b) => Component (a,b) where+  type Storage (a,b) = (Storage a, Storage b)++instance (Has w a, Has w b) => Has w (a,b) where+  {-# INLINE getStore #-}+  getStore = (,) <$> getStore <*> getStore++instance (Store a, Store b) => Store (a,b) where+  type Stores (a, b) = (Stores a, Stores b)+  type SafeRW (a, b) = (SafeRW a, SafeRW b)+  initStore = (,) <$> initStore <*> initStore++  explGet       (sa,sb) ety = (,) <$> explGet sa ety <*> explGet sb ety+  explSet       (sa,sb) ety (wa,wb) = explSet sa ety wa >> explSet sb ety wb+  explReset     (sa,sb) = explReset sa >> explReset sb+  explDestroy   (sa,sb) ety = explDestroy sa ety >> explDestroy sb ety+  explExists    (sa,sb) ety = explExists sa ety >>= \case False -> return False+                                                          True  -> explExists sb ety+  explMembers   (sa,sb) = explMembers sa >>= U.filterM (explExists sb)+  explGetUnsafe (sa,sb) ety = (,) <$> explGetUnsafe sa ety <*> explGetUnsafe sb ety+  explSetMaybe  (sa,sb) ety (wa,wb) = explSetMaybe sa ety wa >> explSetMaybe sb ety wb+  {-# INLINE explGetUnsafe #-}+  {-# INLINE explGet #-}+  {-# INLINE explSet #-}+  {-# INLINE explSetMaybe #-}+  {-# INLINE explMembers #-}+  {-# INLINE explReset #-}+  {-# INLINE explDestroy #-}+  {-# INLINE explExists #-}+--}+tupleInstances :: Int -> Q [Dec]+tupleInstances n = do+  let vars = [ VarT . mkName $ "t_" ++ show i | i <- [0..n-1]]+      tupleUpT = foldl AppT (TupleT n)+      varTuple = tupleUpT vars+      tuplN = tupleDataName n+      tuplE = ConE tuplN+      pureTuplE = AppE (VarE (mkName "pure")) tuplE++      compN = mkName "Component"+      compT var = ConT compN `AppT` var+      strgN = mkName "Storage"+      strgT var = ConT strgN `AppT` var+      compI = InstanceD Nothing (fmap compT vars) (compT varTuple)+        [ TySynInstD strgN $+          TySynEqn [varTuple] (tupleUpT . fmap strgT $ vars)+        ]++      hasN = mkName "Has"+      hasT var = ConT hasN `AppT` VarT (mkName "w") `AppT` var+      getStoreN = mkName "getStore"+      getStoreE = VarE getStoreN+      apN = mkName "<*>"+      apE = VarE apN+      hasI = InstanceD Nothing (hasT <$> vars) (hasT varTuple)+        [ FunD getStoreN+          [Clause [] (NormalB$ liftAll tuplE (replicate n $ getStoreE )) [] ]+        , PragmaD$ InlineP getStoreN Inline FunLike AllPhases+        ]++      liftAll f mas = foldl (\a x -> AppE (AppE apE a) x) (AppE (VarE (mkName "pure")) f) mas+      sequenceAll :: [Exp] -> Exp+      sequenceAll = foldl1 (\a x -> AppE (AppE (VarE$ mkName ">>") a) x)++      strN  = mkName "Store"+      strsN = mkName "Stores"+      safeN = mkName "SafeRW"++      strT  var = ConT strN  `AppT` var+      strsT var = ConT strsN `AppT` var+      safeT var = ConT safeN `AppT` var++      sNs = [ mkName $ "s_" ++ show i | i <- [0..n-1]]+      sPat = ConP tuplN (VarP <$> sNs)+      sEs = VarE <$> sNs+      etyN = mkName "ety"+      etyE = VarE etyN+      etyPat = VarP etyN+      wNs = [ mkName $ "w_" ++ show i | i <- [0..n-1]]+      wPat = ConP tuplN (VarP <$> wNs)+      wEs = VarE <$> wNs++      explGetN       = mkName "explGet"+      explSetN       = mkName "explSet"+      explResetN     = mkName "explReset"+      explDestroyN   = mkName "explDestroy"+      explExistsN    = mkName "explExists"+      explMembersN   = mkName "explMembers"+      explGetUnsafeN = mkName "explGetUnsafe"+      explSetMaybeN  = mkName "explSetMaybe"+      initStoreN     = mkName "initStore"++      explGetE       = VarE explGetN+      explSetE       = VarE explSetN+      explResetE     = VarE explResetN+      explDestroyE   = VarE explDestroyN+      explExistsE    = VarE explExistsN+      explMembersE   = VarE explMembersN+      explGetUnsafeE = VarE explGetUnsafeN+      explSetMaybeE  = VarE explSetMaybeN++      explGetF sE = AppE explGetE sE `AppE` etyE+      explSetF sE wE = AppE explSetE sE `AppE` etyE `AppE` wE+      explResetF sE = AppE explResetE sE+      explDestroyF sE = AppE explDestroyE sE `AppE` etyE+      explExistsF sE = AppE explExistsE sE+      explMembersF sE = AppE explMembersE sE+      explGetUnsafeF sE = AppE explGetUnsafeE sE `AppE` etyE+      explSetMaybeF sE wE = AppE explSetMaybeE sE `AppE` etyE `AppE` wE++      explExistsAnd va vb = AppE (AppE (VarE '(>>=)) va)+                                 (LamCaseE [ Match (ConP 'False []) (NormalB$ AppE (VarE 'return) (ConE 'False)) []+                                           , Match (ConP 'True []) (NormalB vb) []+                                           ])++      explMembersFold va vb = AppE (VarE '(>>=)) va `AppE` AppE (VarE 'U.filterM) vb++      strI = InstanceD Nothing (strT <$> vars) (strT varTuple)+        [ TySynInstD strsN $ TySynEqn [varTuple] (tupleUpT $ fmap strsT vars)+        , TySynInstD safeN $ TySynEqn [varTuple] (tupleUpT $ fmap safeT vars)++        , FunD explGetN [Clause [sPat, etyPat]+            (NormalB$ liftAll tuplE (explGetF <$> sEs)) [] ]+        , PragmaD$ InlineP explGetN Inline FunLike AllPhases++        , FunD explSetN [Clause [sPat, etyPat, wPat]+            (NormalB$ sequenceAll (zipWith explSetF sEs wEs)) [] ]+        , PragmaD$ InlineP explSetN Inline FunLike AllPhases++        , FunD explResetN [Clause [sPat]+            (NormalB$ sequenceAll (explResetF <$> sEs)) [] ]+        , PragmaD$ InlineP explResetN Inline FunLike AllPhases++        , FunD explDestroyN [Clause [sPat, etyPat]+            (NormalB$ sequenceAll (explDestroyF <$> sEs)) [] ]+        , PragmaD$ InlineP explDestroyN Inline FunLike AllPhases++        , FunD explExistsN [Clause [sPat, etyPat]+            (NormalB$ foldr explExistsAnd (AppE (VarE 'pure) (ConE 'True)) ((`AppE` etyE) . explExistsF <$> sEs)) [] ]+        , PragmaD$ InlineP explExistsN Inline FunLike AllPhases++        , FunD explMembersN [Clause [sPat]+            (NormalB$ foldl explMembersFold (explMembersF (head sEs)) (explExistsF <$> tail sEs)) [] ]+        , PragmaD$ InlineP explMembersN Inline FunLike AllPhases++        , FunD explGetUnsafeN [Clause [sPat, etyPat]+            (NormalB$ liftAll tuplE (explGetUnsafeF <$> sEs)) [] ]+        , PragmaD$ InlineP explGetUnsafeN Inline FunLike AllPhases++        , FunD explSetMaybeN [Clause [sPat, etyPat, wPat]+            (NormalB$ sequenceAll (zipWith explSetMaybeF sEs wEs)) [] ]+        , PragmaD$ InlineP explSetMaybeN Inline FunLike AllPhases++        , FunD initStoreN [Clause []+            (NormalB$ liftAll tuplE (VarE initStoreN <$ sEs)) [] ]+        , PragmaD$ InlineP initStoreN Inline FunLike AllPhases++        ]++  return [compI, hasI, strI]
src/Apecs/Types.hs view
@@ -4,6 +4,8 @@ {-# LANGUAGE FlexibleContexts, FlexibleInstances #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE LambdaCase #-}  module Apecs.Types where @@ -11,6 +13,8 @@ import Data.Traversable (for) import qualified Data.Vector.Unboxed as U +import qualified Apecs.THTuples as T+ -- | An Entity is really just an Int. The type variable is used to keep track of reads and writes, but can be freely cast. newtype Entity c = Entity Int deriving (Eq, Ord, Show) @@ -56,10 +60,8 @@   -- | Either writes or deletes a component   explSetMaybe  :: s -> Int -> SafeRW s -> IO () -  -- | The initialization argument required by this store-  type InitArgs s   -- Initialize the store with its initialization arguments.-  initStoreWith :: InitArgs s -> IO s+  initStore :: IO s    -- | Removes all components.   --   Equivalent to calling @explDestroy@ on each member@@ -136,69 +138,7 @@   cast (Slice vec) = Slice vec  -- Tuple Instances--- (,)-instance (Component a, Component b) => Component (a,b) where-  type Storage (a, b) = (Storage a, Storage b)--instance (Has w a, Has w b) => Has w (a,b) where-  {-# INLINE getStore #-}-  getStore = (,) <$> getStore <*> getStore--instance (Store a, Store b) => Store (a,b) where-  type InitArgs (a, b) = (InitArgs a, InitArgs b)-  type Stores (a, b) = (Stores a, Stores b)-  initStoreWith (aa, ab) = (,) <$> initStoreWith aa <*> initStoreWith ab--  explMembers (sa,sb) = explMembers sa >>= U.filterM (explExists sb)-  explReset   (sa,sb) = explReset sa >> explReset sb-  explDestroy (sa,sb) ety = explDestroy sa ety >> explDestroy sb ety-  explExists  (sa,sb) ety = (&&) <$> explExists sa ety <*> explExists sb ety-  {-# INLINE explMembers #-}-  {-# INLINE explReset #-}-  {-# INLINE explDestroy #-}-  {-# INLINE explExists #-}--  type SafeRW (a, b) = (SafeRW a, SafeRW b)-  explGetUnsafe  (sa,sb) ety = (,) <$> explGetUnsafe sa ety <*> explGetUnsafe sb ety-  explGet        (sa,sb) ety = (,) <$> explGet sa ety <*> explGet sb ety-  explSet        (sa,sb) ety (wa,wb) = explSet sa ety wa >> explSet sb ety wb-  explSetMaybe   (sa,sb) ety (wa,wb) = explSetMaybe sa ety wa >> explSetMaybe sb ety wb-  {-# INLINE explGetUnsafe #-}-  {-# INLINE explGet #-}-  {-# INLINE explSet #-}-  {-# INLINE explSetMaybe #-}+T.makeInstances [2..6]  instance (GlobalStore a, GlobalStore b) => GlobalStore (a,b) where---- (,,)-instance (Component a, Component b, Component c) => Component (a,b,c) where-  type Storage (a, b, c) = (Storage a, Storage b, Storage c)-instance (Has w a, Has w b, Has w c) => Has w (a,b,c) where-  {-# INLINE getStore #-}-  getStore = (,,) <$> getStore <*> getStore <*> getStore--instance (Store a, Store b, Store c) => Store (a,b,c) where-  type InitArgs (a, b, c) = (InitArgs a, InitArgs b, InitArgs c)-  type Stores (a, b, c) = (Stores a, Stores b, Stores c)-  initStoreWith (aa, ab, ac) = (,,) <$> initStoreWith aa <*> initStoreWith ab <*> initStoreWith ac--  explMembers (sa,sb,sc) = explMembers sa >>= U.filterM (explExists sb) >>= U.filterM (explExists sc)-  explReset   (sa,sb,sc) = explReset sa >> explReset sb >> explReset sc-  explDestroy (sa,sb,sc) ety = explDestroy sa ety >> explDestroy sb ety >> explDestroy sc ety-  explExists  (sa,sb,sc) ety = and <$> sequence [explExists sa ety, explExists sb ety, explExists sc ety]-  {-# INLINE explMembers #-}-  {-# INLINE explReset #-}-  {-# INLINE explDestroy #-}-  {-# INLINE explExists #-}--  type SafeRW (a, b, c) = (SafeRW a, SafeRW b, SafeRW c)-  explGetUnsafe  (sa,sb,sc) ety = (,,) <$> explGetUnsafe sa ety <*> explGetUnsafe sb ety <*> explGetUnsafe sc ety-  explGet        (sa,sb,sc) ety = (,,) <$> explGet sa ety <*> explGet sb ety <*> explGet sc ety-  explSet        (sa,sb,sc) ety (wa,wb,wc) = explSet sa ety wa >> explSet sb ety wb >> explSet sc ety wc-  explSetMaybe   (sa,sb,sc) ety (wa,wb,wc) = explSetMaybe sa ety wa >> explSetMaybe sb ety wb >> explSetMaybe sc ety wc-  {-# INLINE explGetUnsafe #-}-  {-# INLINE explGet #-}-  {-# INLINE explSet #-}-  {-# INLINE explSetMaybe #-}- instance (GlobalStore a, GlobalStore b, GlobalStore c) => GlobalStore (a,b,c) where
src/Apecs/Util.hs view
@@ -7,7 +7,7 @@   initStore, runGC, unEntity,    -- * EntityCounter-  EntityCounter, initCounter, nextEntity, newEntity,+  EntityCounter, nextEntity, newEntity,    -- * Spatial hashing   -- $hash@@ -22,33 +22,27 @@ import Control.Monad.Reader (liftIO) import Control.Applicative (liftA2) import System.CPUTime+import Data.Monoid  import Apecs.Types import Apecs.Stores import Apecs.System --- | Initializes a store with (), useful since most stores have () as their initialization argument-initStore :: (Store s, InitArgs s ~ ()) => IO s-initStore = initStoreWith ()- unEntity :: Entity a -> Int unEntity (Entity e) = e  -- | Secretly just an int in a newtype-newtype EntityCounter = EntityCounter Int deriving (Num, Eq, Show)+newtype EntityCounter = EntityCounter {getCounter :: Sum Int} deriving (Monoid, Num, Eq, Show)+ instance Component EntityCounter where   type Storage EntityCounter = Global EntityCounter --- | Initialize an EntityCounter-initCounter :: IO (Storage EntityCounter)-initCounter = initStoreWith (EntityCounter 0)- -- | Bumps the EntityCounter and yields its value {-# INLINE nextEntity #-} nextEntity :: Has w EntityCounter => System w (Entity ())-nextEntity = do EntityCounter n <- readGlobal-                writeGlobal (EntityCounter (n+1))-                return (Entity n)+nextEntity = do n <- readGlobal+                writeGlobal (n+1)+                return (Entity . getSum . getCounter $ n)  -- | Writes the given components to a new entity, and yields that entity {-# INLINE newEntity #-}
test/Main.hs view
@@ -5,6 +5,7 @@ {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE DataKinds #-}+{-# LANGUAGE TemplateHaskell #-}  {-# OPTIONS_GHC -w #-} @@ -17,6 +18,7 @@  import Apecs import Apecs.Types+import Apecs.TH import Apecs.Util import qualified Apecs.Stores as S import qualified Apecs.Logs as S@@ -33,12 +35,10 @@ instance Component CachePos where   type Storage CachePos = S.Cache 1 (S.Map CachePos) - newtype Velocity = Velocity Vec deriving (Arbitrary, Eq, Show) instance Component Velocity where   type Storage Velocity = S.Map Velocity - data TestFlag = TestFlag instance Arbitrary TestFlag where arbitrary = return TestFlag instance S.Flag TestFlag where flag = TestFlag@@ -56,30 +56,27 @@ instance Arbitrary (RandomEntity a) where   arbitrary = RandomEntity . Entity . abs <$> arbitrary -newtype W1 c = W1 {w1c1 :: (Storage c)}-instance Component c => Has (W1 c) c where getStore = System $ asks w1c1--data W2 a b = W2 { w2c1 :: Storage a , w2c2 :: Storage b }-instance (Component a, Component b) => Has (W2 a b) a where getStore = System $ asks w2c1-instance (Component a, Component b) => Has (W2 a b) b where getStore = System $ asks w2c2+makeWorld "Counter" [''CachePos]  counter :: [CachePos] -> CachePos -> Property counter cs c = monadicIO $ run f >>= assert   where     f = do-      w :: W2 CachePos EntityCounter <- W2 <$> initStore <*> initCounter+      w <- initCounter       runWith w $ do         forM_ cs newEntity         e <- newEntity c         Safe r <- get e         return (r == Just c) +makeWorld "GetSetPos" [''Position]+ getSetPos :: [(RandomEntity Position, Position)] -> RandomEntity Position -> Position -> Property getSetPos cs (RandomEntity e) p = monadicIO $ run f >>= assert   where     f = do-      w :: Storage Position <- initStore-      runWith (W1 w) $ do+      w <- initGetSetPos+      runWith w $ do         forM_ cs $ \(RandomEntity ety, pos) -> set ety pos         set e p         Safe r <- get e@@ -88,27 +85,28 @@         Members set <- liftIO$ readIORef ref         return (r == Just p && sl1 == U.fromList (S.toList set)) +makeWorld "GetSetVCPos" [''Velocity, ''CachePos]+ getSetVCPos :: [(RandomEntity (Velocity, CachePos), (Velocity, CachePos))] -> RandomEntity (Velocity, CachePos) -> (Velocity, CachePos) -> Property getSetVCPos cs (RandomEntity e) (v,p) = monadicIO $ run f >>= assert   where     f = do-      wp :: Storage CachePos <- initStore-      wv :: Storage Velocity <- initStore-      runWith (W2 wp wv) $ do+      w <- initGetSetVCPos+      runWith w $ do         forM_ cs $ \(RandomEntity ety, pos) -> set ety pos         set e (v,p)         Safe r <- get e         return (r == (Just v, Just p)) +makeWorld "CmapVP" [''Velocity, ''CachePos] cmapVP :: [(RandomEntity (Velocity, CachePos), (Velocity, CachePos))] -> RandomEntity (Velocity, CachePos) -> (Velocity, CachePos) -> Property cmapVP cs (RandomEntity e) (v,p) = monadicIO $ run f >>= assert   where     f = do       let swapP (CachePos (x,y)) = CachePos (y,x)           swapV (Velocity (x,y)) = Velocity (y,x)-      wp :: Storage CachePos <- initStore-      wv :: Storage Velocity <- initStore-      runWith (W2 wp wv) $ do+      w <- initCmapVP+      runWith w $ do         forM_ cs $ \(RandomEntity ety, pos) -> set ety pos         set e (v,p)         cmap $ \(v,p) -> (swapV v, swapP p)