{"id":16722270,"url":"https://github.com/phipsgabler/operajonal","last_synced_at":"2025-03-15T13:21:33.063Z","repository":{"id":82586575,"uuid":"61378069","full_name":"phipsgabler/operajonal","owner":"phipsgabler","description":"Implementation of free monads in JavaScript, based on Haskell's operational package","archived":false,"fork":false,"pushed_at":"2019-10-12T20:00:29.000Z","size":25,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-01-22T03:33:03.485Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","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/phipsgabler.png","metadata":{"files":{"readme":"README.md","changelog":null,"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}},"created_at":"2016-06-17T14:05:52.000Z","updated_at":"2024-11-14T03:13:15.000Z","dependencies_parsed_at":"2023-03-25T01:18:18.030Z","dependency_job_id":null,"html_url":"https://github.com/phipsgabler/operajonal","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/phipsgabler%2Foperajonal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phipsgabler%2Foperajonal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phipsgabler%2Foperajonal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phipsgabler%2Foperajonal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/phipsgabler","download_url":"https://codeload.github.com/phipsgabler/operajonal/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243733336,"owners_count":20339026,"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-12T22:34:09.198Z","updated_at":"2025-03-15T13:21:33.035Z","avatar_url":"https://github.com/phipsgabler.png","language":"JavaScript","readme":"[![No Maintenance Intended](http://unmaintained.tech/badge.svg)](http://unmaintained.tech/)\n\n# Operajonal #\n\nThis package implements a style of free monads which is identical to\n[`operational`](http://hackage.haskell.org/package/operational-0.2.3.2/docs/Control-Monad-Operational.html),\na Haskell package by Heinrich Apfelmus. The biggest difference to other variants of free monads is that\nit uses an intermediate representation for the continuation structure, instead of only `Pure` and `Impure`, which\ncan be transformed into the \"classical\" form, which is here called `ProgramView`.\n\nThis leaves the possibility to write recursive interpreters directly over views, instead of always having to\ntransform commands into a monadic representation (I think this can be more confusing to beginners). However,\nmonadic interpretation is still supported.\n\nAdditionally, a \"do notation\" hack via generator functions is provided. Also, it makes much use of \n[`daggy`](https://github.com/puffnfresh/daggy) to automatically produce nicely usable sum types and allow for\nsome pattern-matching-like syntax.\n\n## Installation ##\n\nCurrently, only\n\n```\nnpm install https://github.com/phipsgabler/operajonal.git\n```\n\n## Example 1: Stacks ##\n\nCan be found in [examples/stacks.js](./examples/stacks.js). Basically, a clone of the stack interpreter\nexample from `operational`, but with an additional command `Add`. First, we need to define the\ncommands we are going to use:\n\n```{JavaScript}\nconst StackInstr = makeInstructions({\n  Push: ['value'],\n  Pop: [],\n  Add: []\n});\n\nconst {Push, Pop, Add} = StackInstr;\n```\n\nUnder the hood, `makeInstructions` produces a `daggy` sum type and wraps its constructors into the `Instr` \nconstructor of `Program`, the internal representation.\n\nNow, suppose this is the monadic program we want to run:\n\n```{JavaScript}\nPush(10).andThen(Push(20))\n    .andThen(Add())\n    .andThen(Push(33))\n    .andThen(Pop()).chain(\n        thirtyTwo =\u003e Pop().chain(\n            sum =\u003e Program.inject([sum, thirtyTwo])));\n```\n\nFor convenience, we can also write that in do-notation:\n\n```{JavaScript}\nconst testProgram = Program.do(function*() {\n  yield Push(10);\n  yield Push(20);\n  yield Add;\n  yield Push(33);\n  let thirtyTwo = yield Pop;\n  let sum = yield Pop;\n  return `last element: ${thirtyTwo}, sum of previous: ${sum}`;\n});\n```\n\nGiven this, we can write a simple recursive interpreter for it. We do not need to manually deal with views, but\ncan directly give a catamorphism to the `interpret` method of `Program`:\n\n```{JavaScript}\nconst interpreter = (program, initialStack) =\u003e program.interpret({\n  Pop: recur =\u003e {\n    const [first, ...rest] = initialStack;\n    return interpreter(recur(first), rest);\n  },\n  Push: (recur, x) =\u003e interpreter(recur({}), [x, ...initialStack]),\n  Add: recur =\u003e {\n    const [first, second, ...rest] = initialStack;\n    const sum = first + second;\n    return interpreter(recur(sum), [sum, ...rest]);\n  }\n});\n```\n\nFor every instruction, we provide a case function taking the current continuation (here called `recur`), and the \nproperties of the instruction. The function should do whatever interpretation is necessary, then call the \ncontinuation on the result, and recursively interpret the new program.\n\nThis interpreter can be run with \n\n```{JavaScript}\nconsole.log(interpreter(testProgram(), []));\n```\n\nand should produce the result\n\n```\nlast element: 33, sum of previous: 30\n```\n\nAlternatively, we can use monadic interpretation, e.g. into the State monad. For that, we do not have to\nwrite our own recursion, but simply translate each instruction into a new monadic action. These will then \nautomatically be chained, and the resulting monadic action can be handled as needed:\n\n```{JavaScript}\nfunction monadicInterpreter(program) {\n  return program.interpretMonadic({\n    Return: State.of,\n    Pop: () =\u003e State.get().chain(stack =\u003e {\n      const [first, ...rest] = stack;\n      return State.put(rest).andThen(State.of(first));\n    }),\n    Push: (x) =\u003e State.modify(stack =\u003e [x, ...stack]),\n    Add: () =\u003e State.get().chain(stack =\u003e {\n      const [first, second, ...rest] = stack;\n      const sum = first + second;\n      return State.put([sum, ...rest]).andThen(State.of(sum));\n    })\n  });\n}\n```\n\nNote that here, we also have to provide a case for `Return`, providing the monad's constructor (it is assumed that\nthe binding method is called `chain`).\n\nWe can run this (assuming our State monad has a `run` method) with: \n\n```{JavaScript}\nconsole.log(monadicInterpreter(testProgram()).run([]));\n```\n\nand it should produce a result equivalent to above.\n\n\n## License ##\n\nThis package is licensed under the MIT license.\n","funding_links":[],"categories":["Libraries"],"sub_categories":["[Javascript](https://developer.mozilla.org/en-US/docs/Web/JavaScript)"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphipsgabler%2Foperajonal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fphipsgabler%2Foperajonal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphipsgabler%2Foperajonal/lists"}