Aztecs: An Empirical Entity Component System (ECS) for Haskell [Draft]
A type-safe and friendly ECS for Haskell. An ECS is a modern approach to organizing your application state as a database, providing patterns for data-oriented design and parallel processing. For more information, please see the documentation on Hackage.
- High-performance: Components are stored by their unique sets in archetypes
- Dynamic components: Scripts and remote interfaces can create unique components with runtime-specified components
- Type-safe DSL: Queries and systems use
Arrow
syntax for compile-time gurantees - Modular design: Aztecs can be extended for a variety of use cases
import Control.Arrow ((>>>))
import Data.Aztecs
import qualified Data.Aztecs.Access as A
import qualified Data.Aztecs.Query as Q
import qualified Data.Aztecs.System as S
newtype Position = Position Int deriving (Show)
instance Component Position
newtype Velocity = Velocity Int deriving (Show)
instance Component Velocity
setup :: System () ()
setup = S.queue . const . A.spawn_ $ bundle (Position 0) <> bundle (Velocity 1)
move :: System () ()
move =
S.map
( proc () -> do
Velocity v <- Q.fetch -< ()
Position p <- Q.fetch -< ()
Q.set -< Position $ p + v
)
>>> S.run print
main :: IO ()
main = runSystem_ $ setup >>> S.forever move
import Control.Arrow (returnA, (>>>))
import Data.Aztecs
import qualified Data.Aztecs.Access as A
import Data.Aztecs.Asset (load)
import qualified Data.Aztecs.Query as Q
import Data.Aztecs.SDL (Image (..), Window (..))
import qualified Data.Aztecs.SDL as SDL
import qualified Data.Aztecs.System as S
import Data.Aztecs.Transform (Transform (..), transform)
import SDL (V2 (..))
setup :: System () ()
setup =
S.mapSingle
( proc () -> do
assetServer <- Q.fetch -< ()
(texture, assetServer') <- Q.run (load "example.png") -< assetServer
Q.set -< assetServer'
returnA -< texture
)
>>> S.queue
( \texture -> do
A.spawn_ $ bundle Window {windowTitle = "Aztecs"}
A.spawn_ $
bundle Image {imageTexture = texture, imageSize = V2 100 100}
<> bundle transform {transformPosition = V2 100 100}
A.spawn_ $
bundle Image {imageTexture = texture, imageSize = V2 200 200}
<> bundle transform {transformPosition = V2 500 100}
)
main :: IO ()
main = runSystem_ $ SDL.setup >>> setup >>> S.forever SDL.update
Aztecs is currently faster than bevy-ecs, a popular and high-performance ECS written in Rust, for simple mutating queries.
![benchmark results: Aztecs 932us vs Bevy 6,966us](https://private-user-images.githubusercontent.com/9288430/406267905-348c7539-0e7b-4429-9cc1-06e8a819156d.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MzkxODIxMzMsIm5iZiI6MTczOTE4MTgzMywicGF0aCI6Ii85Mjg4NDMwLzQwNjI2NzkwNS0zNDhjNzUzOS0wZTdiLTQ0MjktOWNjMS0wNmU4YTgxOTE1NmQucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI1MDIxMCUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNTAyMTBUMTAwMzUzWiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9NDY3MTg3ZTYzYzJmMmQxMTNkYjEzZjk3MWNjMTI4ZjZjYTZmMmVmMDlhMDIzZDMxNTkwMmNjMzRjYWYwN2YzMiZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QifQ.7A9x7zSzeG5RNx0mHd-YV-8-hpbyEdaM4st5CJX7_Ao)
Aztecs' approach to type-safety is inspired by Bevy, but with direct archetype-based storage similar to Flecs.