{"id":13493310,"url":"https://github.com/awto/effectfuljs","last_synced_at":"2025-05-15T09:03:57.929Z","repository":{"id":44710147,"uuid":"58798017","full_name":"awto/effectfuljs","owner":"awto","description":"JavaScript embedded effects compiler","archived":false,"fork":false,"pushed_at":"2025-03-29T11:20:43.000Z","size":16233,"stargazers_count":338,"open_issues_count":23,"forks_count":14,"subscribers_count":17,"default_branch":"main","last_synced_at":"2025-04-07T03:11:19.687Z","etag":null,"topics":["babel","babel-plugin","continuation","javascript","javascript-transpiler","monad","monadic-interface"],"latest_commit_sha":null,"homepage":"https://effectful.js.org","language":"JavaScript","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/awto.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","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},"funding":{"github":["javactrl"]}},"created_at":"2016-05-14T08:35:34.000Z","updated_at":"2025-04-06T17:33:57.000Z","dependencies_parsed_at":"2024-01-16T09:52:05.764Z","dependency_job_id":"7b43d4ea-e2c2-4ded-bb45-fce2ac801699","html_url":"https://github.com/awto/effectfuljs","commit_stats":{"total_commits":457,"total_committers":3,"mean_commits":"152.33333333333334","dds":0.004376367614879695,"last_synced_commit":"1a9cf91caf216f040574104fc427f037b640f2a4"},"previous_names":[],"tags_count":122,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awto%2Feffectfuljs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awto%2Feffectfuljs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awto%2Feffectfuljs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awto%2Feffectfuljs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/awto","download_url":"https://codeload.github.com/awto/effectfuljs/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247622990,"owners_count":20968574,"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":["babel","babel-plugin","continuation","javascript","javascript-transpiler","monad","monadic-interface"],"created_at":"2024-07-31T19:01:14.092Z","updated_at":"2025-04-07T09:07:37.760Z","avatar_url":"https://github.com/awto.png","language":"JavaScript","readme":"# JavaScript embedded effects compiler\n\n[![Build Status](https://dev.azure.com/effectful/js/_apis/build/status/awto.effectfuljs?branchName=main)](https://dev.azure.com/effectful/js/_build/latest?definitionId=1\u0026branchName=main)\n\nThis is a tool to build JavaScript to JavaScript transpilers (babel plugins)\nalong with a few predefined ones. It extends JavaScript language with\nvarious computation effects by means of runtime libraries but without any syntax extension.\n\nThere are such libraries for:\n\n- EcmaScript effects extensions (async functions, generators and\n  async generators)\n  - **_Why not native or other transpilers_**\n    - Abstract API -\n      [@effectful/es](packages/es)\n    - Persistent state -\n      [@effectful/es-persist](packages/es-persist)\n    - Concrete API -\n      [@effectful/es-inline](packages/es-inline),\n      the best performance\n    - Implicit parallelism (experimental)\n    - Deriving program's static graphs for analysis and conversion to other\n      languages - WIP\n- Multi-prompt delimited continuations -\n  [@effectful/cc](packages/cc)\n- Debugger API -\n  [@effectful/debugger](packages/debugger) and VSCode plugin [effectful/debugger](packages/vscode-debugger)\n- Workflow-as-Code on Kafka - [kafka-workflow](https://github.com/awto/kafka-workflow)\n\nNot yet implemented:\n\n- algebraic effects\n- probabilistic programming\n- parallel and distributed programming\n- persistent continuations\n- adaptive computations\n- logical programming - [Old version](https://github.com/awto/mfjs-logic)\n- reactive programming (with RxJS) - [Old version](https://github.com/awto/mfjs-rx)\n\nThey are typically small libraries, some of them are just tiny\nwrappers around well-known interfaces, such as Promises or\nObservables.\n\nThe compiler converts ES2018 to ES2018 and doesn't need any syntax\nextensions. So it may be applied to results of other compilers\ntargeting JavaScript such as CoffeeScript, TypeScript etc.\n\n## Usage\n\nEffectfulJS is a tool to build transpilers. There are many packaging\noptions and usages, but typically the follow next\npattern. Transformations usually contain a babel plugin (and/or macro)\nalong with some runtime. They can be in a single package, with\nEffectful to be a peer dependency (along with a dev dependency). This\nway the transpiler can be installed with:\n\n```\n$ npm install --save \u003clib name\u003e\n$ npm install --save-dev @effectful/core\n```\n\nIf the babel plugin is in `transform.js` of `\u003clib name\u003e` package, `.babelrc` can be:\n\n```\n{\n   \"plugins\": [\u003clib name\u003e/transform]\n}\n```\n\nThe package can also contain `macro.js` file for zero-configuration using\n[babel-plugin-macros](https://github.com/kentcdodds/babel-plugin-macros). This works also for\n[Create Reat App](https://github.com/facebook/create-react-app)) where `.babelrc` is disabled.\n\nCheck the libraries documentation for exact details:\n\n- [@effectful/js](packages/js)\n  compiles to the most abstract code, single or double level depending\n  on parameters\n- [@effectful/es](packages/es)\n  for two-levels syntax either concrete ECMAScript compatible effects\n  or abstract for async, generator and async generators semantics\n  overloading.\n- [@effectful/es-inline](packages/es)\n  A shortcut for [@effectful/es](packages/es)\n  with concrete ECMAScript effects implementations.\n\nAll the plugins and macros are just shortcuts to\n[@effectful/core](packages/es)\nsetting needed lower level options described in\n[config.js](packages/core/src/config.js)\nthus generated code uses different runtime interfaces.\n\n## Examples\n\nThese examples are not the full list of features. The project is\nabstract, with a lot of different applications.\n\n### Persistent state\n\nSave all your application state in DB or file. The state includes the point\nwhere it executes now along with all variables values implementing any\nserialization interface.\n\nRestore the saved state after, maybe in a different process or even on a\ndifferent hardware.\n\nFor the application, the state transfer will be transparent.\n\nThis feature greatly simplifies defining long-running workflows. If the\nprogram requires some not immediate action from a human,\ne.g. email-confirmation, or it should await some exact date. No needs to\nbother implementing all these details, just write a very simple script\ndescribing the business logic and nothing else.\n\nHere's some shop business logic example.\n\n```javascript\nfunction order(customer, item) {\n  begin();\n  customer.balance -= item.price;\n  item.vendor.balance += item.price;\n  scheduleShipment(customer, item);\n  commit();\n}\n```\n\nThe `scheduleShipment` function may require some other people involved. They can\nreject the transaction and so the balances values must be reset to original\nlevels, even if a few days already passed. We don't care about implementation\ndetails. Such as where and how to store the state, how to reset it, etc.\nEverything is done by some underlying generic framework. The framework is not a\nsubject of EffectfulJS though.\n\nThere are a lot of other applications for a persistent state. It can be used\nalso for debugging purposes. This makes it very simple to implement **hot\nreloading** and **time traveling**, as this just saving and restoring the\napplication state at some particular execution points.\n\nMore details are in [@effectful/es](packages/es)\nproject.\n\n### Delimited continuations\n\nThis is the most generic computational effect. Any other can be implemented\nusing it. It looks very similar to Coroutines (async functions and\ngenerators). But unlike Coroutines, it allows resuming computation from the same\npoint more than once.\n\nUnlimited continuation like `callcc` is a special case. They allow capturing all\nand only all execution context. While with delimited continuations it is\npossible to specify only some region. For example, for the persistent state,\nthis allows saving not the whole application state, which may be big, but only\nsome interesting part.\n\nCheck [@effectful/cc](packages/es) for\nmore details.\n\n## Implicit parallelism\n\nAsync functions considerably simplify asynchronous source code. But it\nserializes operation execution. Next operation executes only after the current\nis finished. If we want to run the operations in parallel we can use\n`Promise.all`.\n\nSay, we have two async operations (probably some remote server requests) - `A`\nand `B`:\n\n```javascript\nasync function a() {\n  const a = await A;\n  const b = await B;\n}\n```\n\nIf these operations are independent they can be executed in parallel:\n\n```javascript\nasync function a() {\n  const [a, b] = await Promise.all([A, B]);\n}\n```\n\nThis doesn't look more complex, but what if we make `B` dependent on `A` and add\nanother operation `C`:\n\n```javascript\nasync function parDemo() {\n  const a = await A;\n  const b = await B(a);\n  const c = await C;\n}\n```\n\nNow it is impossible to start `B` before `A` is finished, but we can start `C`\nin parallel with `A` and `B`:\n\n```javascript\nasync function parDemo() {\n  const [a, c] = await Promise.all([A, C]);\n  const b = await B(a);\n}\n```\n\nThis version is not effective, operation `C` may take much longer than `A`\nand this will prevent `B` from starting execution. The correct version is:\n\n```javascript\nasync function parDemo() {\n  let a;\n  const [b, c] = await Promise.all([\n    (async () =\u003e {\n      a = await A;\n      b = await B(a);\n    })(),\n    C\n  ]);\n}\n```\n\nThis is obviously significantly more complex, error-prone, harder to read and\nmaintain. It will become even much worse with some next small changes. They may\nrequire almost full function rewrite.\n\nFortunately, EffectfulJS can do the transformation automatically, just specify\nwhich block you want to parallelize. The transpiler will automatically compute\nvariables dependencies and structure the block parts in `Promise.all`\naccordingly.\n\nHere is how the last example looks with EffectfulJS:\n\n```javascript\nasync function parDemo() {\n  \"par\";\n  const a = await A;\n  const b = await B(a);\n  const c = await C;\n}\n```\n\nSwitching between parallel and sequential code is just a matter of adding \"seq\"/\"par\"\ndirectives.\n\nThis works for loops too. With the help of a **persistent state** some jobs may\nbe delegated to **WebWorker** or some **cloud server**, making simple but\neffective distributed programs.\n\n### Reactive programming (WIP)\n\nReactive programs operate with values which change with time. While in\nthe source code it looks like a single value variable:\n\n```javascript\nfunction drawBox(root) {\n  const down = event(root, \"mousedown\");\n  if (down \u0026\u0026 down.button === 0) {\n    const move = event(root, \"mousemove\");\n    const up = event(root, \"up\");\n    const cur = move || up;\n    const box = (\n      \u003cdiv className={up ? \"box\" : \"drawing\"}\u003e\n        style=\n        {{\n          left: Math.min(down.x, cur.x) + pageXOffset,\n          top: Math.min(down.y, cur.y) + pageYOffset,\n          width: Math.abs(down.x - cur.x),\n          height: Math.abs(down.y - cur.y)\n        }}\n        \u003e\n      \u003c/div\u003e\n    );\n    const del = event(box, \"contextMenu\");\n    if (!del) root.appendChild(box);\n    if (!move) return;\n  }\n}\n```\n\nHere all the variables may change when some mouse event is received, while the\nprogram looks like it is executed only once. The framework is responsible for\nrecalculating dependant parts of the program. Such programs are much easier to\nwrite, test, debug and maintain.\n\n### Logical programming (WIP)\n\nThis is a library for adding logical programming languages features to\nJavaScript. Most Prolog books start from classical bi-directional list append\nfunction. Depending on input arguments it may either append two lists or split\nsome list in two. Here is an almost literal translation of its implementation in\nJavaScript:\n\n```javascript\nfunction append(a, b, r) {\n  const [h, t, rt] = newRefs();\n  unify(a, cons(h, t));\n  unify(r, cons(h, rt));\n  append(t, b, rt);\n  M.or();\n  unify(a, nil());\n  unify(r, b);\n}\n```\n\nFor front-end, this, for example, can be useful to specify some control's values\nconstraints. In the next example, there are three cyclically dependent fields,\nnamely payment amount without discount, with discount and the discount\nvalue. The end-user may enter two of them while the third will be calculated\nautomatically.\n\n```javascript\nfunction PaymentForm() {\n  const [cost, discount, finalCost] = newRefs();\n  constraint(finalCost === (cost * discount) / 100);\n  return (\n    \u003cform action=\"/pay\"\u003e\n      \u003cinput type=\"text\" name=\"cost\" value={cost} /\u003e\n      \u003cinput type=\"text\" name=\"discount\" value={discount} /\u003e\n      \u003cinput type=\"text\" name=\"finalCost\" value={finalCost} /\u003e\n      \u003cinput type=\"submit\" value=\"Pay\" /\u003e\n    \u003c/form\u003e\n  );\n}\n```\n\nThe same can be done with the new React hooks, which is an implementation of\ncomputational effect relying on runtime only. Transpiler like EffectfulJS\ndoesn't have runtime-only limitations, namely,\n[React Hooks Rules](https://reactjs.org/docs/hooks-rules.html).\nThey significantly reduce the number of useful use cases. Some complex program\nlogic may be simplified with loops, conditions and other common JavaScript\nconstructs.\n\n## How it works\n\nThe compiler stratifies input JavaScript code into two levels. The\nfirst level (_pure_) contains JS constructs (expressions, statements)\nkept as is in generated code. The second level (_effectful_) contains\neffectful constructs. The compiler converts them into abstract\nfunction calls.\n\nI'm abusing term _pure_ here. The statements or expressions on this\nlevel can have any side effect already implemented in\nJavaScript. Namely IO, references, exceptions, loops, breaks, returns,\netc.\n\nThe levels may be either explicit or implicit in syntax (two-level or\nsingle-level syntax resp.).\n\nAsync functions or generators syntax can be used for marking\nexpressions of explicit _effectful_ level.\n\n```javascript\nasync function a() {\n  console.log(\"x:\", await getX());\n}\n```\n\nUnlike standard ECMAScript async function this will be converted to\nabstract API calls. The API is\n\nThe overloading may be either compile time - implementing some syntax\ntransforms, or runtime - providing libraries with abstract interfaces\nimplementations.\n\nThis is an example of one of a few possible outputs:\n\n```javascript\nfunction a() {\n  return M.chain(getX(), _1);\n  function _1(a) {\n    console.log(\"x:\", a);\n  }\n}\n```\n\nThere `chain`, `raise` methods are provided by some runtime library.\nFor example, its concrete implementation for Promises calls `then`\nfunction for `chain` and rejects the promise in `M.raise`.\n\nThe names and interfaces of the functions in the generated code are\nconfigurable.\n\nThere is a dozen of such functions required to be implemented in\nconcrete effects implementation library but, usually, most of them are\ntrivial.\n\nThe interface is based on Monads. However understanding Monads is\nrequired only to implement some new effects library. No needs to know\nanything about them to use the libraries.\n\nWith the implicit single-level mode the same code may be even more\nsuccinct:\n\n```javascript\nfunction() {\n  console.log(\"x:\",getX());\n}\n```\n\nThere are more examples of input/output in the\n[test folder](packages/js/test/samples).\n\nIt is arguable if explicit or implicit levels separation is\nbetter. This likely depends on what kind of effects is used. The\nsuccinct and more readable code is good, but if effects are heavy\nmaking them explicit may be better. So EffectfulJS compiler supports\nboth options.\n\n## LICENSE\n\nDistributed under the terms of The MIT License (MIT).\n","funding_links":["https://github.com/sponsors/javactrl"],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fawto%2Feffectfuljs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fawto%2Feffectfuljs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fawto%2Feffectfuljs/lists"}