{"id":18763150,"url":"https://github.com/simre1/hero","last_synced_at":"2026-03-07T03:02:52.469Z","repository":{"id":54565012,"uuid":"522319180","full_name":"Simre1/hero","owner":"Simre1","description":"Haskell ECS using sparse sets","archived":false,"fork":false,"pushed_at":"2022-09-03T20:07:28.000Z","size":660,"stargazers_count":13,"open_issues_count":4,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-13T04:44:58.913Z","etag":null,"topics":["ecs","entity-component-system","game-development","haskell"],"latest_commit_sha":null,"homepage":"","language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Simre1.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-08-07T20:37:54.000Z","updated_at":"2024-12-27T00:18:24.000Z","dependencies_parsed_at":"2022-08-13T19:50:47.710Z","dependency_job_id":null,"html_url":"https://github.com/Simre1/hero","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Simre1/hero","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Simre1%2Fhero","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Simre1%2Fhero/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Simre1%2Fhero/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Simre1%2Fhero/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Simre1","download_url":"https://codeload.github.com/Simre1/hero/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Simre1%2Fhero/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30206339,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-06T19:07:06.838Z","status":"online","status_checked_at":"2026-03-07T02:00:06.765Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ecs","entity-component-system","game-development","haskell"],"created_at":"2024-11-07T18:24:53.501Z","updated_at":"2026-03-07T03:02:52.453Z","avatar_url":"https://github.com/Simre1.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Hero\n\nHero is an entity component system in Haskell and is inspired by [_apecs_](https://github.com/jonascarpay/apecs). Hero aims to be more performant than _apecs_ by using sparse sets as the main data structure for storing component data. In the future, it also intends to parallelize systems automatically as well.\n\n## Entities\n\nEntities are objects which have components. You can create entities with the `createEntity` system and add components to them.\n\n## Components\n\nComponents are Haskell datastructure and mostly contain raw data.\n\n```haskell\ndata Position = Position Float Float\n```\n\nIn order to use `Position` as a component, you need to make it an instance of `Component`.\n\n```haskell\ninstance Component Position where\n  type Store Position = BoxedSparseSet \n  -- StorableSparseSet is faster but Position would need to implement Storable\n```\n\n## Stores\n\nEach component has a store which holds the component data. Depending on the use of the component, different stores might be best. \nThe most important stores are:\n- `BoxedSparseSet`: Can be used with any datatype. Each entity has its own component value.\n- `StorableSparseSet`: Can be used with `Storable` datatypes. Each entity has its own component value. Faster than `BoxedSparseSet`.\n- `Global`: Can be used with any datatype. Each entity has the same component value. Can also be accessed without an entity.\n\n## Systems\n\nSystems are functions which operate on the components of a world. For example, you can map the `Position` component of every entity:\n\n```haskell\ncmap_ $ \\(Position x y) -\u003e Position (x + 1) y\n```\n\n`System`s have the type `system :: System input output`. `System`s can be chained together through their `Applicative`, `Category` and `Arrow` instance. However, `System`s dot have an instance for `Monad`! If one is familiar with arrowized FRP, then they will feel that `System`s have a similar interface as signal functions.\n\n`System`s do not have a `Monad` instance since they are separated into a compilation and a run phase. Before you can run a `System`, you need to compile it with `compileSystem :: System m input output -\u003e World -\u003e IO (input -\u003e IO output)`. In the compilation phase, `System`s look up the used components so that run time is faster.\n\n## Example\n\n```haskell\n{-# LANGUAGE TypeFamilies #-}\nimport Control.Monad ( forM_ )\nimport Hero\nimport Data.Foldable (for_)\n\ndata Position = Position Int Int\n\ndata Velocity = Velocity Int Int\n\ninstance Component Position where\n  type Store Position = BoxedSparseSet\n\ninstance Component Velocity where\n  type Store Velocity = BoxedSparseSet\n\nmain :: IO ()\nmain = do\n  world \u003c- createWorld 10000\n  runSystem \u003c- compileSystem system world\n  runSystem ()\n\nsystem :: System () ()\nsystem =\n  \n  -- Create two entities\n  (pure (Position 0 0, Velocity 1 0) \u003e\u003e\u003e createEntity) *\u003e\n  (pure (Position 10 0, Velocity 0 1) \u003e\u003e\u003e createEntity) *\u003e\n  \n  -- Map position 10 times\n  for_ [1..10] (\\_ -\u003e cmap_ (\\(Position x y, Velocity vx vy) -\u003e Position (x + vx) (y + vy))) *\u003e\n\n  -- Print the current position\n  cmapM_ (\\(Position x y) -\u003e print (x,y))\n```\n\n## Installation\n\nTo download the project and execute it, you need at least _GHC 9_. I have tested it with _GHC 9.2.1_.\n\nThen, you can run the following commands to build the project.\n```\ngit clone https://github.com/Simre1/hero\ncd hero\ncabal build all\n```\n\nTo run the example, do:\n```\ncabal run example\n```\n\nTo run the tests, do:\n```\ncabal test\n```\n\nTo run the benchmarks, do:\n```\ncabal run hero-bench\ncabal run apecs-bench\n```\n\nTo generate **documentation**, do:\n```\ncabal haddock\ncabal haddock hero-sdl2\n```\n\n### Hero SDL2\n\nThe folder hero-sdl2 contains a small libary to use SDL2 with Hero. It needs the C libraries `SDL2`, `SDL2_gdx` and `SDL2_image`.\n\nThe Hero SDL2 examples can be run with:\n\n```\ncabal run rotating-shapes\ncabal run image-rendering\ncabal run move-shape\n```\n\nMore information can be found in [`hero-sdl2`](hero-sdl2/README.md).\n\n### Cabal dependency\n\nTo use _Hero_ as a dependency, add `hero` to the `build-depends` section. Additional, create a `cabal.project` with: \n```\npackages: *.cabal\n\nsource-repository-package\n   type: git\n   location: https://github.com/Simre1/hero\n```\n\nIf you also want to use `hero-sdl2`, add `hero-sdl2` to the `build-depends` section as well. The `cabal.project` is then:\n```\npackages: *.cabal\n\nsource-repository-package\n   type: git\n   location: https://github.com/Simre1/hero\n\nsource-repository-package\n   type: git\n   location: https://github.com/Simre1/hero\n   subdir: hero-sdl2\n```\n\n## Benchmark\n\nA basic benchmark seems to suggest that `Hero` is much faster. However, it relies heavily on\nGHC inlining. It is best to use concrete types when working with systems or add an `INLINE` pragma to \nfunctions which deal with polymorphic systems.\n\nThe following queries are used to test the iteration speed of both libraries:\n```\ncmap_ (\\(Velocity vx vy, Acceleration ax ay) -\u003e Velocity (vx + ax) (vy + ay)) *\u003e\ncmap_ (\\(Position x y, Velocity vx vy) -\u003e Position (x + vx) (y + vy))\n```\n\n### Hero\n\n```\n  simple physics (3 components): OK (0.20s)\n    394  μs ±  25 μs\n```\n\n### Apecs\n\n```\n  simple physics (3 components): OK (0.13s)\n    17.5 ms ± 1.5 ms\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimre1%2Fhero","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsimre1%2Fhero","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimre1%2Fhero/lists"}