{"id":17251140,"url":"https://github.com/fumieval/oath","last_synced_at":"2025-04-14T06:08:07.998Z","repository":{"id":66326287,"uuid":"434421500","full_name":"fumieval/oath","owner":"fumieval","description":"Composable Concurrent Computation Done Right","archived":false,"fork":false,"pushed_at":"2023-01-03T14:45:04.000Z","size":16,"stargazers_count":30,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-14T06:08:01.319Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","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/fumieval.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-12-03T00:52:11.000Z","updated_at":"2024-10-14T03:43:06.000Z","dependencies_parsed_at":null,"dependency_job_id":"94ea4aa4-16b5-4090-8b96-29f0bac00dfd","html_url":"https://github.com/fumieval/oath","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fumieval%2Foath","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fumieval%2Foath/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fumieval%2Foath/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fumieval%2Foath/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fumieval","download_url":"https://codeload.github.com/fumieval/oath/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248830397,"owners_count":21168272,"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":[],"created_at":"2024-10-15T06:50:28.495Z","updated_at":"2025-04-14T06:08:07.963Z","avatar_url":"https://github.com/fumieval.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"Oath: composable concurrent computation done right\n----\n\nOath is an Applicative structures that makes concurrent actions composable.\n\n```haskell\nnewtype Oath a = Oath { runOath :: forall r. (STM a -\u003e IO r) -\u003e IO r }\n```\n\n`Oath` is a continuation-passing IO action which takes a transaction to obtain the final result (`STM a`).\nThe continuation-passing style makes it easier to release resources in time.\nThe easiest way to construct `Oath` is `oath`. It runs the supplied IO action in a separate thread as long as the continuation is running.\n\n```haskell\noath :: IO a -\u003e Oath a\noath act = Oath $ \\cont -\u003e do\n  v \u003c- newEmptyTMVarIO\n  tid \u003c- forkFinally act (atomically . putTMVar v)\n  let await = takeTMVar v \u003e\u003e= either throwSTM pure\n  cont await `finally` killThread tid\n\nevalOath :: Oath a -\u003e IO a\nevalOath m = runOath m atomically\n```\n\n`Oath` is an `Applicative`, so you can combine multiple `Oath`s using `\u003c*\u003e`. `Oath` combined this way kicks off computations without waiting for the results. The following code runs `foo :: IO a` and `bar :: IO b` concurrently, then applies `f` to these results.\n\n```haskell\nmain = evalOath $ f \u003c$\u003e oath foo \u003c*\u003e oath bar\n```\n\nIt _does not_ provide a Monad instance because it is logically impossible to define one consistent with the Applicative instance.\n\nUsage\n----\n\n`Oath` abstracts a triple of sending a request, waiting for response, and cancelling a request. If you want to send requests in a deterministic order, you can construct `Oath` directly instead of calling `oath`.\n\n```haskell\nOath $ \\cont -\u003e bracket sendRequest cancelRequest (cont . waitForResponse)\n```\n\nTimeout behaviour can be easily added using the `Alternative` instance and `delay :: Int -\u003e Oath ()`. `a \u003c|\u003e b` runs both computations until one of them returns a result, then cancels the other.\n\n```haskell\n-- | An 'Oath' that finishes once the given number of microseconds elapses\ndelay :: Int -\u003e Oath ()\n\noath action \u003c|\u003e delay 100000\n```\n\nComparison to other packages\n----\n\n[future](https://hackage.haskell.org/package/future-2.0.0/docs/Control-Concurrent-Future.html), [caf](https://hackage.haskell.org/package/caf-0.0.3/docs/Control-Concurrent-Futures.html) and [async](https://hackage.haskell.org/package/async-2.2.4/docs/Control-Concurrent-Async.html) seem solve the same problem. They define abstractions to asynchronous computations. `async` has an applicative `Concurrently` wrapper.\n\n[spawn](https://hackage.haskell.org/package/spawn-0.3/docs/Control-Concurrent-Spawn.html) does not define any datatype. Instead it provides an utility function for `IO` (`spawn :: IO a -\u003e IO (IO a)`). It does not offer a way to cancel a computation.\n\n[promises](https://hackage.haskell.org/package/promises-0.3/docs/Data-Promise.html) provides a monadic interface for pure demand-driven computation. It has nothing to do with concurrency.\n\n[unsafe-promises](https://hackage.haskell.org/package/unsafe-promises-0.0.1.3/docs/Control-Concurrent-Promise-Unsafe.html) creates an IO action that waits for the result on-demand using `unsafeInterleaveIO`.\n\n[futures](https://hackage.haskell.org/package/futures-0.1/docs/Futures.html) provides a wrapper of `forkIO`. There is no way to terminate an action and it does not propagate exceptions.\n\n[promise](https://hackage.haskell.org/package/promise-0.1.0.0/docs/Control-Concurrent-Promise.html) has illegal Applicative and Monad instances; `(\u003c*\u003e)` is not associative and its `ap` is not consistent with `(\u003c*\u003e)`.\n\nPerformance\n----\n\n```haskell\nbench \"oath 10\" $ nfIO $ O.evalOath $ traverse (O.oath . pure) [0 :: Int ..9]\nbench \"async 10\" $ nfIO $ A.runConcurrently $ traverse (A.Concurrently . pure) [0 :: Int ..9]\n```\n\n`Oath`'s overhead of `(\u003c*\u003e)` is less than `Concurrently`. Unlike `Concurrently`, `\u003c*\u003e` itself does not fork threads.\n\n```\nAll\n  oath 10:   OK (1.63s)\n    5.78 μs ± 265 ns\n  async 10:  OK (0.21s)\n    12.3 μs ± 767 ns\n  oath 100:  OK (0.22s)\n    52.6 μs ± 4.4 μs\n  async 100: OK (0.23s)\n    109  μs ± 8.4 μs\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffumieval%2Foath","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffumieval%2Foath","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffumieval%2Foath/lists"}