{"id":20819581,"url":"https://github.com/calmm-js/freer","last_synced_at":"2025-05-07T15:23:00.881Z","repository":{"id":57241635,"uuid":"150236099","full_name":"calmm-js/freer","owner":"calmm-js","description":"A free Static Land compatible Monad implementation","archived":false,"fork":false,"pushed_at":"2019-01-06T09:11:48.000Z","size":127,"stargazers_count":13,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-05-01T13:44:33.445Z","etag":null,"topics":["async","effects","free","functional","monad","reader","state"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/calmm-js.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-09-25T09:00:00.000Z","updated_at":"2025-02-11T12:55:04.000Z","dependencies_parsed_at":"2022-09-07T23:13:24.375Z","dependency_job_id":null,"html_url":"https://github.com/calmm-js/freer","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/calmm-js%2Ffreer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/calmm-js%2Ffreer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/calmm-js%2Ffreer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/calmm-js%2Ffreer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/calmm-js","download_url":"https://codeload.github.com/calmm-js/freer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252903070,"owners_count":21822366,"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":["async","effects","free","functional","monad","reader","state"],"created_at":"2024-11-17T22:06:49.393Z","updated_at":"2025-05-07T15:23:00.854Z","avatar_url":"https://github.com/calmm-js.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# \u003ca id=\"freer\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/freer/index.html#) [Freer](#freer) \u0026middot; [![Gitter](https://img.shields.io/gitter/room/calmm-js/chat.js.svg)](https://gitter.im/calmm-js/chat) [![GitHub stars](https://img.shields.io/github/stars/calmm-js/freer.svg?style=social)](https://github.com/calmm-js/freer) [![npm](https://img.shields.io/npm/dm/freer.svg)](https://www.npmjs.com/package/freer)\n\nThis is an *experimental* JavaScript library that implements a Free (or Freer)\nmonad with composable effect handlers.\n\n**WARNING:** This library is not yet ready for production use.\n\n[![npm version](https://badge.fury.io/js/freer.svg)](http://badge.fury.io/js/freer)\n[![Build Status](https://travis-ci.org/calmm-js/freer.svg?branch=master)](https://travis-ci.org/calmm-js/freer)\n[![Code Coverage](https://img.shields.io/codecov/c/github/calmm-js/freer/master.svg)](https://codecov.io/github/calmm-js/freer?branch=master)\n[![](https://david-dm.org/calmm-js/freer.svg)](https://david-dm.org/calmm-js/freer)\n[![](https://david-dm.org/calmm-js/freer/dev-status.svg)](https://david-dm.org/calmm-js/freer?type=dev)\n\n## \u003ca id=\"contents\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/freer/index.html#contents) [Contents](#contents)\n\n* [Examples](#examples)\n  * [Getting started](#getting-started)\n  * [Running sum of leaves](#running-sum-of-leaves)\n* [Reference](#reference)\n  * [Free monad](#free-monad)\n    * [`F.Free ~\u003e monad`](#F-Free)\n  * [Free combinators](#free-combinators)\n    * [`F.map(value =\u003e value, free) ~\u003e free`](#F-map)\n    * [`F.of(value) ~\u003e free`](#F-of)\n    * [`F.ap(free, free) ~\u003e free`](#F-ap)\n    * [`F.chain(value =\u003e free, free) ~\u003e free`](#F-chain)\n  * [Identity handler](#identity-handler)\n    * [`F.run(free) ~\u003e value`](#F-run)\n    * [`F.runAsync(free) ~\u003e promise`](#F-runAsync)\n  * [Do notation](#do-notation)\n    * [`F.from(async $ =\u003e { ... await $(free) ... }) ~\u003e free`](#F-from)\n    * [`F.toAsync(free) ~\u003e free`](#F-toAsync)\n  * [Defining new handlers](#defining-new-handlers)\n    * [`F.handler((value, any) =\u003e free, (effect, continuation, any) =\u003e free) ~\u003e (free, any) ~\u003e free`](#F-handler)\n  * [Handlers à la carte](#handlers-a-la-carte)\n    * [`F.Exception([{concat[, empty]}]) ~\u003e {raise, handle[, zero], alt, alts, run}`](#F-Exception)\n    * [`F.Reader() ~\u003e {ask, local, run}`](#F-Reader)\n    * [`F.State() ~\u003e {get, put, modify, run}`](#F-State)\n* [Related work](#related-work)\n\n## \u003ca id=\"examples\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/freer/index.html#examples) [Examples](#examples)\n\n### \u003ca id=\"getting-started\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/freer/index.html#getting-started) [Getting started](#getting-started)\n\nFirst we create some handlers and free operations:\n\n```js\nvar aReader = F.Reader()\nvar aState = F.State()\n```\n\nThen we define an ad-hoc operation that uses the previously defined free\noperations:\n\n```js\nvar addReaderToState = F.from(async $ =\u003e {\n  const v = await $(aReader.ask)\n  return $(aState.modify(R.add(v)))\n})\n```\n\nThe above uses a [`do` notation](#do-notation) approximation provided by this\nlibrary.  One could also define the above operation using just the basic\n[monadic combinators](#free-combinators).\n\nThen we compose a runner that handles the operations we used:\n\n```js\nvar aRunner = R.compose(F.runAsync, aState.run(1), aReader.run(2), F.toAsync)\n```\n\nFinally we run the operation and log the result:\n\n```js\naRunner(addReaderToState).then(console.log)\n// Promise 3\n```\n\n### \u003ca id=\"running-sum-of-leaves\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/freer/index.html#running-sum-of-leaves) [Running sum of leaves](#running-sum-of-leaves)\n\nHere is an example using\n[`traverse`](https://github.com/calmm-js/partial.lenses/#L-traverse) from\nPartial Lenses to compute a running sum of the leaves of a nested data\nstructure:\n\n```js\nR.compose(F.run, aState.run(0))(\n  L.traverse(\n    F.Free,\n    x =\u003e aState.modify(R.add(x)),\n    L.leafs,\n    [{x: 3, y: [1]}, {z: [4, 1]}]\n  )\n)\n// [{x: 3, y: [4]}, {z: [8, 9]}]\n```\n\n## \u003ca id=\"reference\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/freer/index.html#reference) [Reference](#reference)\n\nThe [combinators](https://wiki.haskell.org/Combinator) provided by this library\nare provided as named exports.  Typically one just imports this library as:\n\n```jsx\nimport * as F from 'freer'\n```\n\nThe examples also make use of the [Partial\nLenses](https://github.com/calmm-js/partial.lenses/) and\n[Ramda](https://github.com/calmm-js/partial.lenses/) libraries imported as:\n\n```jsx\nimport * as L from 'partial.lenses'\nimport * as R from 'ramda'\n```\n\nNeither of those libraries is required in order to use this library.\n\n### \u003ca id=\"free-monad\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/freer/index.html#free-monad) [Free monad](#free-monad)\n\n#### \u003ca id=\"F-Free\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/freer/index.html#F-Free) [`F.Free ~\u003e monad`](#F-Free)\n\n`F.Free` is the [Static\nLand](https://github.com/rpominov/static-land/blob/master/docs/spec.md)\ncompatible\n[`Monad`](https://github.com/rpominov/static-land/blob/master/docs/spec.md#monad)\ndefinition for the\n[monad](https://github.com/rpominov/static-land/blob/master/docs/spec.md#monad)\nprovided by this library.\n\n### \u003ca id=\"free-combinators\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/freer/index.html#free-combinators) [Free combinators](#free-combinators)\n\n#### \u003ca id=\"F-map\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/freer/index.html#F-map) [`F.map(value =\u003e value, free) ~\u003e free`](#F-map)\n\n`F.map` is the [Static\nLand](https://github.com/rpominov/static-land/blob/master/docs/spec.md)\ncompatible\n[`map`](https://github.com/rpominov/static-land/blob/master/docs/spec.md#functor)\ncombinator of the\n[monad](https://github.com/rpominov/static-land/blob/master/docs/spec.md#monad)\nprovided by this library.\n\n#### \u003ca id=\"F-of\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/freer/index.html#F-of) [`F.of(value) ~\u003e free`](#F-of)\n\n`F.of` is the [Static\nLand](https://github.com/rpominov/static-land/blob/master/docs/spec.md)\ncompatible\n[`of`](https://github.com/rpominov/static-land/blob/master/docs/spec.md#applicative)\ncombinator of the\n[monad](https://github.com/rpominov/static-land/blob/master/docs/spec.md#monad)\nprovided by this library.\n\n#### \u003ca id=\"F-ap\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/freer/index.html#F-ap) [`F.ap(free, free) ~\u003e free`](#F-ap)\n\n`F.ap` is the [Static\nLand](https://github.com/rpominov/static-land/blob/master/docs/spec.md)\ncompatible\n[`ap`](https://github.com/rpominov/static-land/blob/master/docs/spec.md#apply)\ncombinator of the\n[monad](https://github.com/rpominov/static-land/blob/master/docs/spec.md#monad)\nprovided by this library.\n\n#### \u003ca id=\"F-chain\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/freer/index.html#F-chain) [`F.chain(value =\u003e free, free) ~\u003e free`](#F-chain)\n\n`F.chain` is the [Static\nLand](https://github.com/rpominov/static-land/blob/master/docs/spec.md)\ncompatible\n[`chain`](https://github.com/rpominov/static-land/blob/master/docs/spec.md#chain)\ncombinator of the monad provided by this library.\n\n### \u003ca id=\"identity-handler\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/freer/index.html#identity-handler) [Identity handler](#identity-handler)\n\n#### \u003ca id=\"F-run\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/freer/index.html#F-run) [`F.run(free) ~\u003e value`](#F-run)\n\n`F.run` is the identity handler for the free monad.  It doesn't handle any\neffects per se, it just extracts the result of the computation.\n\n#### \u003ca id=\"F-runAsync\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/freer/index.html#F-runAsync) [`F.runAsync(free) ~\u003e promise`](#F-runAsync)\n\n`F.runAsync` is the asynchronous identity handler for the free monad.  It only\nhandles promises.\n\n### \u003ca id=\"do-notation\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/freer/index.html#do-notation) [Do notation](#do-notation)\n\n#### \u003ca id=\"F-from\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/freer/index.html#F-from) [`F.from(async $ =\u003e { ... await $(free) ... }) ~\u003e free`](#F-from)\n\n`F.from` is used to wrap an `async $ =\u003e { ... }` function that `await $( ... )`s\nfor effects, reminescent of a [`do`\nnotation](https://en.wikibooks.org/wiki/Haskell/do_notation), as a free\noperation to be handled by [`F.toAsync`](#F-toAsync).\n\n#### \u003ca id=\"F-toAsync\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/freer/index.html#F-toAsync) [`F.toAsync(free) ~\u003e free`](#F-toAsync)\n\n`F.toAsync` is the handler for the operations produced by [`F.from`](#F-from)\nand converts those effects to promises.  This handler must be before handlers\nfor effects used in the operations produced by [`F.from`](#F-from) and the\npromises need to be handled by [`F.runAsync`](#F-runAsync).  In other words, the\nhandler composition should look like `R.compose(F.runAsync, ..., F.toAsync)`.\n\n### \u003ca id=\"defining-new-handlers\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/freer/index.html#defining-new-handlers) [Defining new handlers](#defining-new-handlers)\n\n#### \u003ca id=\"F-handler\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/freer/index.html#F-handler) [`F.handler((value, any) =\u003e free, (effect, continuation, any) =\u003e free) ~\u003e (free[, any]) ~\u003e free`](#F-handler)\n\n`F.handler` defines a handler for some effects.  The first argument is the\nhandler for the final result.  The second argument is the handler for some\neffects.  It is given an effect, which the handler may or many not know how to\nhandle, and the continuation and it must then return a free operation.\n\n### \u003ca id=\"handlers-a-la-carte\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/freer/index.html#handlers-a-la-carte) [Handlers à la carte](#handlers-a-la-carte)\n\n#### \u003ca id=\"F-Exception\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/freer/index.html#F-Exception) [`F.Exception([{concat[, empty]}]) ~\u003e {raise, handle[, zero], alt, alts, run}`](#F-Exception)\n\n`F.Exception` is a factory for Exception handling effects.  The optional\nargument is a\n[semigroup](https://github.com/rpominov/static-land/blob/master/docs/spec.md#semigroup)\nor\n[monoid](https://github.com/rpominov/static-land/blob/master/docs/spec.md#monoid)\nused for combining exception values in `alt` and `alts`.  In case the argument\nis just a semigroup, `alts` requires at least one operation and there will be no\n`zero`.  The default argument is a semigroup that uses the last exception value.\n\nGiven `const Ex = F.Exception()`,\n\n* `Ex.raise(any) ~\u003e free` raises the given value to the closest enclosing\n  `Ex.handle`r or to the top of the handler stack,\n* `Ex.handle(any =\u003e free, free) ~\u003e free` handles values raised from the given\n  operation,\n* `Ex.zero ~\u003e free` is equivalent to `Ex.raise(empty())` when the argument to\n  `F.Exception` is a monoid,\n* `Ex.alts(...free) ~\u003e free` tries the given operations in turn and returns the\n  result of the first operation that completes without raising or raises the\n  values accumulated from all the operations that raised,\n* `Ex.alt(free, free) ~\u003e free` is a curried binary version of `Ex.alts`, and\n* `Ex.run` is the handler for the operations.\n\nFor example:\n\n```js\nconst Ex = F.Exception()\n\nR.compose(F.run, Ex.run)(\n  Ex.alt(\n    Ex.raise(`You'll never see me!`),\n    Ex.handle(\n      e =\u003e Ex.raise(`Nor me!`),\n      F.of(`This is what you'll get!`)\n    )\n  )\n)\n// 'This is what you'll get!'\n```\n\n#### \u003ca id=\"F-Reader\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/freer/index.html#F-Reader) [`F.Reader() ~\u003e {ask, local, run}`](#F-Reader)\n\n`F.Reader` is a factory for Reader effects.\n\nGiven `const Rd = F.Reader()`,\n\n* `Rd.ask ~\u003e free` is an operation whose result is the value from the reader,\n* `Rd.local(value =\u003e value, free) ~\u003e free` returns an operation that runs the\n  given operation with the value of the reader modified with the given function,\n  and\n* `Rd.run(value, free)` is the handler for the operations.\n\nFor example:\n\n```js\nconst Rd = F.Reader()\n\nR.compose(F.run, Rd.run(1))(\n  F.ap(Rd.local(R.inc, F.map(R.add, Rd.ask)), Rd.ask)\n)\n// 3\n```\n\n#### \u003ca id=\"F-State\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/freer/index.html#F-State) [`F.State() ~\u003e {get, put, modify, run}`](#F-State)\n\n`F.State` is a factory for State effects.\n\nGiven `const St = F.State()`,\n\n* `St.get ~\u003e free` is an operation whose result is the current state,\n* `St.put(value) ~\u003e free` is an operation that replaces the state with the given\n  value,\n* `St.modify(value =\u003e value) ~\u003e free` is operation that updates the state with\n  the given function and whose result is the new state, and\n* `St.run` is the handler for the operations.\n\nFor example:\n\n```js\nconst St = F.State()\n\nR.compose(F.run, St.run(undefined))(\n  L.traverse(\n    F.Free,\n    it =\u003e F.chain(\n      prev =\u003e (it === prev ? F.of(undefined) : St.modify(R.always(it))),\n      St.get\n    ),\n    L.elems,\n    [1, 2, 2, 1, 1, 3]\n  )\n)\n// [1, 2, 1, 3]\n```\n\n## \u003ca id=\"related-work\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/freer/index.html#reference) [Related work](#related-work)\n\nThe core of this library is based on ideas from [Extensible Effects: an\nalternative to Monad Transformers](http://okmij.org/ftp/Haskell/extensible/).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcalmm-js%2Ffreer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcalmm-js%2Ffreer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcalmm-js%2Ffreer/lists"}