{"id":24692604,"url":"https://github.com/gipphe/caseof","last_synced_at":"2025-03-22T02:14:03.977Z","repository":{"id":32808523,"uuid":"143164280","full_name":"Gipphe/caseof","owner":"Gipphe","description":"A case expression-like implementation for javascript","archived":false,"fork":false,"pushed_at":"2023-04-21T10:58:32.000Z","size":436,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-01-26T20:16:40.714Z","etag":null,"topics":["caseof","functional-programming","javascript","pattern-matching","sanctuary"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/Gipphe.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":"2018-08-01T14:05:17.000Z","updated_at":"2022-05-19T10:50:41.000Z","dependencies_parsed_at":"2025-01-26T20:16:42.124Z","dependency_job_id":"f33f1ff5-2736-4e37-8820-7acf4b7780d6","html_url":"https://github.com/Gipphe/caseof","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gipphe%2Fcaseof","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gipphe%2Fcaseof/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gipphe%2Fcaseof/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gipphe%2Fcaseof/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Gipphe","download_url":"https://codeload.github.com/Gipphe/caseof/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244894316,"owners_count":20527677,"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":["caseof","functional-programming","javascript","pattern-matching","sanctuary"],"created_at":"2025-01-26T20:16:39.475Z","updated_at":"2025-03-22T02:14:03.928Z","avatar_url":"https://github.com/Gipphe.png","language":"TypeScript","readme":"# caseof\n\n\u003e Elm and Haskell-inspired case-of\n\n[![Package status](https://img.shields.io/npm/v/caseof.svg?style=flat-square)](https://www.npmjs.com/package/caseof)\n[![Build status](https://img.shields.io/circleci/project/github/Gipphe/caseof.svg?style=flat-square)](https://circleci.com/gh/Gipphe/caseof)\n[![Code coverage](https://img.shields.io/coveralls/Gipphe/caseof.svg?style=flat-square)](https://coveralls.io/github/Gipphe/caseof)\n[![License](https://img.shields.io/github/license/Gipphe/caseof.svg?style=flat-square)](https://opensource.org/licenses/MIT)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg?style=flat-square)](https://conventionalcommits.org)\n\nA tiny package imitating a case-of expression from languages like Elm and Haskell.\n\n- [Getting Started](#getting-started)\n  - [Installing](#installing)\n  - [Usage](#usage)\n- [Motivation](#motivation)\n- [Contribution](#contribution)\n- [Compatibility](#compatibility)\n- [Changelog](#changelog)\n- [Versioning](#versioning)\n- [License](#license)\n- [Further reading](#further-reading)\n\n## Getting started\n\n### Installing\n\nUse the following command to add it to your project:\n\n```shell\nnpm install --save caseof\n```\n\n### Usage\n\n\u003c!--transcribe--\u003e\n\n### \u003ca name=\"otherwise\" href=\"https://github.com/Gipphe/caseof/blob/master/src/index.ts#L25\"\u003e`otherwise :: a -⁠\u003e Boolean`\u003c/a\u003e\n\nSimply a function that always returns true. Will always be considered a\n\"match\", and its handler will always be executed if it is encountered.\nIt is mainly useful as a `default` case.\n\n```javascript\n\u003e otherwise ()\ntrue\n\n\u003e otherwise ('foo')\ntrue\n\n\u003e caseOf ((when) =\u003e {\n.   when (x =\u003e x \u003c 20) (x =\u003e x + 10)\n.   when (otherwise) (() =\u003e 0)\n. }) (23)\n0\n```\n\n### \u003ca name=\"caseOfAll\" href=\"https://github.com/Gipphe/caseof/blob/master/src/index.ts#L46\"\u003e`caseOfAll :: ((a -⁠\u003e Boolean) -⁠\u003e (a -⁠\u003e b) -⁠\u003e Undefined) -⁠\u003e a -⁠\u003e Array b`\u003c/a\u003e\n\nReturns the result of all matching cases' handlers. The order will be\nthe same order as `when` was called.\n\n```javascript\n\u003e caseOf.all ((when) =\u003e {\n.   when (x =\u003e typeof x === 'number') (x =\u003e x + 1)\n.   when (x =\u003e typeof x === 'number') (x =\u003e x - 0)\n. }) (1)\n[2, 0]\n\n\u003e caseOf.all ((when) =\u003e {\n.   when (x =\u003e x \u003e 0) (x =\u003e x - 1)\n.   when (x =\u003e x \u003c 0) (x =\u003e x + 1)\n. }) (-4)\n[-3]\n```\n\nLike `caseOf`, this function throws if none of the cases match.\n\n```javascript\n\u003e caseOf.all ((when) =\u003e {\n.  when (x =\u003e x \u003e 3) (x =\u003e x)\n. }) (0)\n! Error None of the cases matches the value\n```\n\n### \u003ca name=\"caseOf\" href=\"https://github.com/Gipphe/caseof/blob/master/src/index.ts#L83\"\u003e`caseOf :: ((a -⁠\u003e Boolean) -⁠\u003e (a -⁠\u003e b) -⁠\u003e Undefined) -⁠\u003e a -⁠\u003e b`\u003c/a\u003e\n\nReturns the result of the first matching case. This function is lazy,\nand only the first matching handler is run.\n\n```javascript\n\u003e caseOf ((when) =\u003e {\n.   when (x =\u003e x === 'foo') (x =\u003e x + 'bar')\n.   when (x =\u003e typeof x === 'string') (x =\u003e x.toUpperCase ())\n. }) ('foo')\n'foobar'\n\n\u003e let fn = caseOf ((when) =\u003e {\n.   when (x =\u003e x % 15 === 0) (() =\u003e 'FizzBuzz')\n.   when (x =\u003e x % 3 === 0) (() =\u003e 'Fizz')\n.   when (x =\u003e x % 5 === 0) (() =\u003e 'Buzz')\n.   when (() =\u003e true) (x =\u003e x) // these two\n.   when (caseOf.otherwise) (x =\u003e x) // are equivalent\n. })\n\n\u003e fn (1)\n1\n\n\u003e fn (3)\n'Fizz'\n\n\u003e fn (5)\n'Buzz'\n\n\u003e fn (15)\n'FizzBuzz'\n```\n\nThis function throws an error if none of the cases match.\n\n```javascript\n\u003e caseOf ((when) =\u003e {\n.   when (x =\u003e x === 'foo') (x =\u003e x + 'bar')\n. }) ('quack')\n! Error: None of the cases matches the value\n```\n\n\u003c!--/transcribe--\u003e\n\n## Motivation\n\nHaving used [sanctuary](https://sanctuary.js.org/) quite a lot, we found it\nannoying having to repeat this kind of pattern:\n\n```javascript\nfunction whatFoo(x) {\n\tif (x === 3) {\n\t\treturn \"foo\";\n\t}\n\tif (x \u003e 3) {\n\t\treturn \"bar\";\n\t}\n\tif (x \u003c 3) {\n\t\treturn \"foobar\";\n\t}\n\treturn \"quack\";\n}\n```\n\nInevitably, even with monads encapsulating most of this kind of control flow\nfor you, you might inevitably find yourself repeating this kind of\n\"if-stacking\" pattern.\n\nTo me, this is rather verbose and ugly, and there are a lot of things being\nrepeated in terms of statements. Keep in mind though: there is absolutely\nnothing inherently _wrong_ with this kind of \"if-stacking\".\n\nBecause of this, and because we simply adore [Elm](http://elm-lang.org/) and\n[Haskell](https://www.haskell.org/)'s `case x of` expressions (aka simply\n\"Case expressions\"), we saw it necessary to implement something along those\nlines in Javascript.\n\nSo, instead of the \"ugly\" if-stacking above, we can write\n\n```javascript\nfunction whatFoo(x) {\n\treturn caseOf((when) =\u003e {\n\t\twhen((x) =\u003e x === 3)(() =\u003e \"foo\");\n\t\twhen((x) =\u003e x \u003e 3)(() =\u003e \"bar\");\n\t\twhen((x) =\u003e x \u003c 3)(() =\u003e \"foobar\");\n\t\twhen(() =\u003e true)(() =\u003e \"quack\");\n\t})(x);\n}\n```\n\nBut, as you can see, `caseOf` (and `caseOf.all`) is a curried function,\nmeaning is not a function that takes two arguments, like `function fn(x, y)`.\nNo no, it is a function that takes one argument, and then returns another\nfunction, which subsequently takes another argument:\n\n```javascript\nfunction fn(x) {\n  return function(y) {\n    ...\n  }\n}\n```\n\nThis allows us to just asign to `whatFoo`, instead of wrapping `caseOf`:\n\n```javascript\nvar whatFoo =\n\tcaseOf((when) =\u003e {\n\t\twhen((x) =\u003e x === 3)(() =\u003e \"foo\");\n\t\twhen((x) =\u003e x \u003e 3)(() =\u003e \"bar\");\n\t\twhen((x) =\u003e x \u003c 3)(() =\u003e \"foobar\");\n\t\twhen(() =\u003e true)(() =\u003e \"quack\");\n\t}) \u003e whatFoo(3);\n(\"foo\");\n```\n\nAnd the predicate functions can just be references, like when you partially\napply a function from sanctuary or ramda:\n\n```javascript\nvar S =\n\trequire(\"sanctuary\") \u003e\n\tcaseOf((when) =\u003e {\n\t\twhen(S.equals(3))(S.K(\"foo\"));\n\t\twhen(S.gt(3))(S.K(\"bar\"));\n\t\twhen(S.lt(3))(S.K(\"foobar\"));\n\t\twhen(S.K(true))(S.K(\"quack\"));\n\t})(3);\n(\"foo\");\n```\n\nGive it a little more breathing room,\n[like you might do in a language where spaces are function application](https://github.com/sanctuary-js/sanctuary/issues/438),\nand you've got yourself some easily readable code:\n\n```javascript\nvar S =\n\trequire(\"sanctuary\") \u003e\n\tcaseOf((when) =\u003e {\n\t\twhen(S.equals(3))(S.K(\"foo\"));\n\n\t\twhen(S.gt(3))(S.K(\"bar\"));\n\n\t\twhen(S.lt(3))(S.K(\"foobar\"));\n\n\t\twhen(S.K(true))(S.K(\"quack\"));\n\t})(3);\n(\"foo\");\n```\n\nThat is, \"easily readable\" if you're used to a more Haskell-like style. How\nyou want to style your code is up to you.\n\n## Contribution\n\nThis package is open to pull requests. To set up the development environment,\nfork it, clone it, and run\n\n```shell\nyarn\n```\n\nin the project folder. This will install all necessary dependencies.\n\nRun the command\n\n```shell\nyarn test\n```\n\nto run unit tests.\n\nThis project uses a slightly altered variant of\n[sanctuary-style](http://github.com/sanctuary-js/sanctuary-style). You can\nlint the project using\n\n```shell\nyarn lint\n```\n\n## Compatibility\n\nThis package is compatible all the way down to Node 6 and IE9. It might be\ncompatible with older versions of Node/IE, but such guarantees cannot be made.\n\n## Type checking\n\nThis library features no actual type checking, other than that you call\n`caseOf` and `caseOf.all` as curried functions, as well as that `when` is\npassed actual functions as arguments.\n\nAs previously mentioned in the \"motivation\" section, this library is heavily\ninspired by the work of [sanctuary-js](https://github.com/sanctuary-js), but\nthere is one thing that is hard to integrate from their work: `sanctuary-def`\ntype definitions. `caseOf` technically has a type signature of\n`caseOf :: ((a -\u003e Boolean) -\u003e (a -\u003e b) -\u003e Undefined) -\u003e a -\u003e b`, but this type\nsignature is in practice completely impossible to enforce, even with the help\nof `sanctuary-def`. This is because the `when` function that is passed to the\nfirst argument of `caseOf` cannot be expected to check type signatures of each\npredicate and handler, given it only calls the handler of which the predicate\nis satisfied. Meaning this\n\n```javascript\n\u003e let fn = caseOf ((when) =\u003e {\n.   when (x =\u003e x === 'foo') (x =\u003e x + 'bar')\n.   when (x =\u003e x \u003e 3) (x =\u003e x - 10)\n. })\n\n\u003e fn (14)\n4\n\n\u003e fn ('foo')\n'foobar'\n```\n\nis perfectly valid, and will never complain, even if we utilized\n`sanctuary-def` in some way (which we didn't). This is a similar problem you\nwill see with `sanctuary`'s own `ifElse`:\n\n```javascript\n\u003e let fn = S.ifElse\n.   (x =\u003e x !== 'foo')\n.   (() =\u003e 'bar')\n.   (() =\u003e 34)\n\n\u003e fn ('foo')\n34\n\n\u003e fn (1)\n'bar'\n```\n\nThis is valid according to its signature,\n`ifElse :: (a -\u003e Boolean) -\u003e (a -\u003e b) -\u003e (a -\u003e b) -\u003e a -\u003e b`, because in each\ncase, `b` isn't checked for the function that doesn't get called. For input\n`'foo'`, `b` is `String`, but the result of the `else` function isn't checked\nunless it is run (unless it is a function defined with `sanctuary-def`). And\nfor the second case, where input is `'bar'`, `b` is `Number`, but the `then`\nfunction isn't checked.\n\nAll in all, this is a limitation of runtime type checking.\n\n## Versioning\n\nWe use [SemVer](http://semver.org/) for versioning. For the versions available,\nsee [the tags on this repository](https://github.com/Gipphe/caseof/tags).\n\n## License\n\nThis project is licensed under the MIT License - see the\n[LICENSE.md file](https://github.com/Gipphe/caseof/blob/master/LICENSE.md) for\ndetails.\n\n## Further reading\n\n- [Elm Case-expression example](http://elm-lang.org/examples/case)\n- [Haskell Case-expression examples](http://zvon.org/other/haskell/Outputsyntax/caseQexpressions_reference.html)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgipphe%2Fcaseof","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgipphe%2Fcaseof","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgipphe%2Fcaseof/lists"}