{"id":18345042,"url":"https://github.com/chrispenner/eve","last_synced_at":"2025-04-13T09:42:42.112Z","repository":{"id":55927358,"uuid":"83854944","full_name":"ChrisPenner/eve","owner":"ChrisPenner","description":"An extensible event-driven application framework in haskell","archived":false,"fork":false,"pushed_at":"2020-12-06T18:48:00.000Z","size":397,"stargazers_count":110,"open_issues_count":2,"forks_count":9,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-03-27T01:11:13.689Z","etag":null,"topics":["application-framework","event-driven","event-loop","extensible","framework","haskell-library"],"latest_commit_sha":null,"homepage":null,"language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ChrisPenner.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":"2017-03-04T01:03:56.000Z","updated_at":"2025-02-19T07:46:10.000Z","dependencies_parsed_at":"2022-08-15T09:40:22.152Z","dependency_job_id":null,"html_url":"https://github.com/ChrisPenner/eve","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChrisPenner%2Feve","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChrisPenner%2Feve/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChrisPenner%2Feve/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChrisPenner%2Feve/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ChrisPenner","download_url":"https://codeload.github.com/ChrisPenner/eve/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248693820,"owners_count":21146863,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["application-framework","event-driven","event-loop","extensible","framework","haskell-library"],"created_at":"2024-11-05T21:06:59.905Z","updated_at":"2025-04-13T09:42:42.065Z","avatar_url":"https://github.com/ChrisPenner.png","language":"Haskell","readme":"Eve\n===\n\n[![Join the chat at https://gitter.im/eve-framework/Lobby](https://badges.gitter.im/eve-framework/Lobby.svg)](https://gitter.im/eve-framework/Lobby?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n[![Hackage](https://img.shields.io/badge/hackage-latest-green.svg)](https://hackage.haskell.org/package/eve)\n\nAn extensible event-driven application framework in haskell for building embarassingly modular software.\n\nDocumentation\n-------------\nYou can find hackage documentation for eve [HERE](https://hackage.haskell.org/package/eve)\n\nGetting started\n---------------\n### [Building A Game in Eve](https://github.com/ChrisPenner/eve/blob/master/examples/tunnel-crawler/README.md)\n[Here's](https://github.com/ChrisPenner/eve/blob/master/examples/tunnel-crawler/README.md) a guide which\nwalks you through building your first application in Eve from start to finish, it's quite thorough and it's a great\nplace to start!\n\nIf you have any issues (and I'm sure there'll be a few; it's a new project!)\nplease report them [here](https://github.com/ChrisPenner/eve/issues).\n\nCore Principles\n---------------\n\nEve's core principle is making it easy to build programs in a modular way.\nThere are two key concepts in Eve which you should be aware of:\n\n- Events\n- State\n\n## Events\nEve provides many useful combinators for dispatching events and adding\nlisteners to events, events are a broad concept in Eve and can be triggered by\nuser-interaction, file-changes, even network sockets! Anything you can think of\nreally! Each time an event is fired, your app 'reacts' by running any\nassociated listeners on the given event. \n\nThe functions you need to know are (with simplified types, see the real type in\nthe [hackage docs](https://hackage.haskell.org/package/eve/docs/Eve.html)):\n\n- `dispatchEvent :: forall eventType result m. (Monad m, Monoid result) =\u003e eventType -\u003e m result`\n- `addListener :: forall eventType result m. (Monad m, Monoid result) (eventType -\u003e m result) -\u003e m ListenerId`\n\nAs I mention above, these types are simplified a bit (and yet they still look\ncomplicated!). Actually, the types look so complex so that they're simpler to\nuse! The `forall` makes it so that you can call `dispatchEvent` with ANY\nTypeable type and it will run the proper event listeners which were registered\nby `addListener`; those listeners can alter app state, or even dispatch more\nevents! If the listeners return some (monoidal) value then the results from all\nlisteners are combined with `mappend` and are returned. That's pretty much it!\n\nHere's a quick example for those who need to see some code:\n\n```haskell\nimport Eve\nimport Data.Monoid\n\n-- Define an event to listen for, in this case we don't even need any data alongside it.\ndata ComputeScore = ComputeScore\n\n-- Define some computations which calculate some aspect of score.\n-- We accept an argument of 'ComputeScore' to define what this is a listener for\nscoreContributor1, scoreContributor2 :: ComputeScore -\u003e App (Sum Int)\nscoreContributor1 _ = do\n  ... -- do some calculation over app state to determine one aspect of score\n  return (Sum score)\n\nscoreContributor2 _ = do\n  ... -- Calculate some other aspect of the score\n  return (Sum score)\n\n-- In eve's initialization block we register the listeners, we could add these listeners anywhere\nmain :: IO ()\nmain = eve_ $ do\n  ... -- other initialization (e.g. key listeners, etc.)\n  addListener_ scoreContributor1\n  addListener_ scoreContributor2\n\n  -- This dispatches the triggering event and monoidally sums all the individual score components!\ncomputeTotalScore :: App (Sum Int)\ncomputeTotalScore = do\n  Sum score \u003c- dispatchEvent ComputeScore\n  return score\n```\n\n## State\n\nNext we see how Eve handles state. Eve seeks to be as extensible as possible so\nit makes very few assumptions about the type of state that you (or your\nextensions) plan to store. You can define a type of state yourself using `data`\nand then provide actions which alter that state using a `MonadState` instance\n(from mtl). Don't worry if you don't know what that means, here's a real quick\nexample which uses the combinators from the [lens\nlibrary](https://hackage.haskell.org/package/lens) to make a few simple state\nchanges.\n\n```haskell\nimport Eve\nimport Control.Lens\ndata MyState = MyState\n  { _myInt :: Int\n  , _myString :: String\n  }\nmakeLenses ''MyState\n\n-- This alters some state and returns the old string for some reason.\ndoSomething :: Action MyState String\ndoSomething = do\n  oldString \u003c- use myString\n  myString .= \"Hi!\"\n  myInt += 1\n  return oldString\n```\n\nSo what does this gain us? Well now if we have a `MyState` somewhere in our app we\ncan run that Action on it! We can also register that Action as a listener for some\nevent!\n\nNow for the interesting part; handling state for extensions. This is usually a bit\ntricky since the types that an extension might use aren't known by you (the app author).\nEve takes care of this by providing an interface for extensions to store and keep track\nof arbitrary types, while still allowing other extensions to run actions that it exports.\nThis is where the `HasStates` typeclass comes in; here's the honest to goodness implementation:\n\n```haskell\nclass HasStates s  where\n  states :: Lens' s States\n```\n\nIf your state implements that typeclass, then extensions can store their own states inside it!\nIt's pretty easy to implement too, let's add it to our `MyState`.\n\n```haskell\nimport Eve\nimport Control.Lens\ndata MyState = MyState\n  { _myInt :: Int\n  , _myString :: String\n  , _myStates :: States\n  }\nmakeLenses ''MyState\n\ninstance HasStates MyState where\n    states = myStates\n```\n\nDone! We added a new field which has the type `States` which is exported by Eve.\nThen we just took the **lens** created by `makeLenses` and used it in our instance.\nThat's it! Now extensions can store their own state inside `Action MyState` by\nusing the `stateLens`; check out the [hackage docs](https://hackage.haskell.org/package/eve/docs/Eve.html) \non that for more info on how to do it!\n\nThose are the basics, but you can do much more than that if you like!\nEve also lets you add listeners and dispatch events on an Object specific basis!\nIf you have a copy of some state (let's say a single instance of an Enemy in a game)\nyou can dispatch events over that enemy individually and any registered (Action Enemy)\ncallbacks will be run without affecting any other enemies! Check out `HasEvents`\nto see how that works.\n\nOne last cool feature is that event listeners can return information! If your event\nlistener results in a return value that's a Monoid (like a list, or string for example)\nyou can collect the responses of all the listeners when you call `dispatchEvent`. This\nis a great way for your application to 'ask' extensions about their state.\n\nWhen designing applications in Eve; it's crucial to think about how the\nstate of you application will be stored, and how different components interact.\nEve works best when components are separated and communicate with each-other through\nevents. This is because it allows those who will eventually write extensions to your\napplication to 'hook' into those events to add functionality.\n\nThere are some definite Pros and Cons to Eve's approach:\n\n### Pros\n\n-   Implementing most core functionality using the event system your app remains extensible.\n-   Flexibility \u0026 Adaptability; applications can be written in such a way that\n    users can replace entire components with alternate versions.\n\n### Cons\n\n-   Module cross-dependencies makes the community infrastructure more fragile,\n-   This architecture takes some getting used-to.\n\nContributing\n============\n\nInstallation\n------------\n\nEve uses Stack for reproducible builds.\n\n1. Install [stack](http://seanhess.github.io/2015/08/04/practical-haskell-getting-started.html)\n3. Clone this repo and `cd` into the directory\n4. Run `stack build`\n\nRunning Tests\n-------------\n\n- `stack test`\n\nContributions\n-------------\n\nChatting about features is a key part of Eve's development; come join us in\nthe [Chat Room](https://gitter.im/eve-framework/Lobby) to discuss features or \nimprovements!\n\nRelated Works\n=============\n\n- [Emulating traditional imperative event loops with functional paradigms](./paper/functional-event-loops.pdf)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrispenner%2Feve","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchrispenner%2Feve","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrispenner%2Feve/lists"}