{"id":21439471,"url":"https://github.com/missingfaktor/match-block","last_synced_at":"2025-06-19T04:39:04.364Z","repository":{"id":15548725,"uuid":"18283708","full_name":"missingfaktor/match-block","owner":"missingfaktor","description":"[ABANDONED] Pattern matching blocks as values","archived":false,"fork":false,"pushed_at":"2015-11-08T23:25:22.000Z","size":168,"stargazers_count":4,"open_issues_count":1,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-17T00:17:02.553Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Clojure","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/missingfaktor.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":"2014-03-31T06:19:57.000Z","updated_at":"2018-12-12T08:39:08.000Z","dependencies_parsed_at":"2022-09-19T09:01:49.830Z","dependency_job_id":null,"html_url":"https://github.com/missingfaktor/match-block","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/missingfaktor/match-block","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/missingfaktor%2Fmatch-block","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/missingfaktor%2Fmatch-block/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/missingfaktor%2Fmatch-block/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/missingfaktor%2Fmatch-block/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/missingfaktor","download_url":"https://codeload.github.com/missingfaktor/match-block/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/missingfaktor%2Fmatch-block/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260689666,"owners_count":23047046,"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-11-23T00:41:53.646Z","updated_at":"2025-06-19T04:38:59.351Z","avatar_url":"https://github.com/missingfaktor.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# match-block\n\n## NOTICE: This project has been abandoned.\n\n### Pattern matching blocks as values.\n\n\u003cbr/\u003e\n![data all the things](http://i.imgur.com/6OupF6Q.jpg)\n\nTurning abstractions into first-class values gives us an ability to abstract over and compose them, and often yields conceptually simpler models. First-class-ing things has been a common theme in Clojure, where it's fondly referred to as \"data all the things\".\n\nPattern matching is a lovely feature. It's an integral part of all major functional languages. Sadly, in most implementations, pattern matching blocks aren't first class values.\n\nScala innovates in this area by treating pattern-matching blocks as first-class values. It achieves this by providing a dedicated type (called `PartialFunction`) for them. Let's see how this can be useful.\n\n## PartialFunction in Scala\n\nConsider the following piece of Scala code (pasted directly from a REPL session):\n\n```scala\nscala\u003e def foo(a: String, b: String) = try {\n     |   a.toInt / b.toInt\n     | } catch {\n     |   case ex: NumberFormatException =\u003e 'nfe\n     |   case ex: ArithmeticException =\u003e 'ae\n     | }\nfoo: (a: String, b: String)Any\n\nscala\u003e foo(\"2\", \"1\")\nres6: Any = 2\n\nscala\u003e foo(\"kl\", \"1\")\nres7: Any = 'nfe\n\nscala\u003e foo(\"9\", \"0\")\nres8: Any = 'ae\n```\n\nSyntactically, the `try`-`catch` here looks fairly similar to its Clojure counterpart. However there is one big difference. The bit that's passed to `catch` as argument is a first-class value! This lets us do things like:\n\n```scala\nscala\u003e def foo(a: String, b: String, handler: PartialFunction[Throwable, Any]) = try {\n     |   a.toInt / b.toInt\n     | } catch handler\nfoo: (a: String, b: String, handler: PartialFunction[Throwable,Any])Any\n\nscala\u003e val h: PartialFunction[Throwable, Any] = {\n     |   case ex: NumberFormatException =\u003e 'nfe\n     | }\nh: PartialFunction[Throwable,Any] = \u003cfunction1\u003e\n\nscala\u003e val i: PartialFunction[Throwable, Any] = {\n     |   case ex: ArithmeticException =\u003e 'ae\n     | }\ni: PartialFunction[Throwable,Any] = \u003cfunction1\u003e\n\nscala\u003e foo(\"4\", \"0\", h.orElse(i))\nres9: Any = 'ae\n```\n\nAnd even:\n\n```scala\nscala\u003e attempt {\n     |   val s = Console.readLine\n     |   s.toInt\n     | } fallback {\n     |   case ex: NumberFormatException =\u003e println(\"Invalid string. Try again.\"); restart\n     | }\n\n// \"hobo\"\nInvalid string. Try again.\n\n// \"kucuk\"\nInvalid string. Try again.\n\n// \"9\"\nres12: Int = 9\n\nscala\u003e\n```\n\n*(You can find the implementation for the `attempt`-`fallback` utility [here](http://blog.engineering.vayana.in/).)*\n\nFrom these examples, we can deduce two major advantages of first-class pattern matching blocks:\n\n- One can compose pattern matching blocks using combinators such as `orElse`.\n- It's easy to create new constructs requiring case-based handling, without having to resort to ad hoc syntactic transformations (macros).\n\nThere are numerous examples in the Scala world where this has been put to a good use. Some of which are as follows:\n\n- [`scala.util.control.Exception`](http://www.scala-lang.org/api/current/index.html#scala.util.control.Exception$) - Compositional and functional goodness, atop traditional exception handling constructs.\n- Standard collection operations like `collect`.\n- Methods such as [`onSuccess`](http://docs.scala-lang.org/overviews/core/futures.html#callbacks) for registering callbacks in futures library.\n- Error recovery combinators, such as [`recover`](http://docs.scala-lang.org/overviews/core/futures.html#functional_composition_and_forcomprehensions) in futures library.\n- [`react`](http://docs.scala-lang.org/overviews/core/actors.html) block in actors.\n- [Request matchers](http://simply.liftweb.net/index-Chapter-11.html) in Lift.\n\n## How things look on the Clojure side\n\nClojure currently does not have a generic construct of this sort. Clojure's `try`-`catch` for example, is an ad hoc syntax, which maps almost directly to its Java counterpart.\n\nAs it happens, Clojure has everything you will need to implement this idea on your own:\n\n- First-class functions.\n- [`core.match`](https://github.com/clojure/core.match) - a great pattern matching library to piggyback on.\n- Support for building syntactic extensions (by virtue of being a Lisp).\n\nThis project uses the above to provide first-class pattern matching blocks implementation for Clojure.\n\nThe implementation is almost entirely based on Scala's `PartialFunction` - the core implementation, the optimizations, and the combinators.\n\n## What the project currently does\n\nThe REPL session below should demystify the crux of the library:\n\n```clojure\nuser=\u003e (use '[clojure.core.match :only (match)]) (use 'match-block.core)\nnil\nnil\nuser=\u003e (macroexpand-1 '(match-block [a b] [2 :two] :qux [3 :three] :guz))\n(match-block.core/map-\u003eMatchBlock {:fun         (clojure.core/fn [a b]\n                                                  (clojure.core.match/match [a b]\n                                                                            [2 :two] :qux\n                                                                            [3 :three] :guz))\n                                   :defined-at? (clojure.core/fn [a b]\n                                                  (clojure.core.match/match [a b]\n                                                                            [2 :two] true\n                                                                            [3 :three] true\n                                                                            :else false))})\n\n```\n\n\n## Future directions for the library\n\n- Combinators to compose, transform pattern matching blocks. Examples: `or-else`, `comp`, `apply-or-else`, `lift`, `unlift`, `cond` etc.\n- Scala's `PartialFunction`s have more special treatment in compiler, making the above-mentioned combinators very efficient. We could borrow some of those ideas in this port.\n- A variant of `try`-`catch` that accepts its handler as a pattern matching block.\n- The [`slingshot`](https://github.com/scgilardi/slingshot) library has a concept of \"selectors\". I think \"selectors\" are simply a special case of \"matching\", and matching should belong in `core.match`. The selectors could likely be reimplemented with a bunch of custom `core.match` patterns, plus `match-block`.\n- The implementation could potentially make use of knowledge of `core.match` innards to provide faster implementations of `:fun` and `:defined-at?`.\n\n\n## Usage\n\n(REPL session again.)\n\n```clojure\nuser=\u003e (use '[clojure.core.match :only (match)]) (use 'match-block.core)\nnil\nnil\nuser=\u003e (def foo\n  #_=\u003e   (match-block [a b]\n  #_=\u003e                [3 1] :nice\n  #_=\u003e                :else :aww-shucks))\n#'user/foo\nuser=\u003e (foo 3 1)\n:nice\nuser=\u003e (foo 3 2)\n:aww-shucks\nuser=\u003e (defined-at? foo 3 1)\ntrue\nuser=\u003e (defined-at? foo 3 3)\ntrue\nuser=\u003e (def bar\n  #_=\u003e   (match-block [a b]\n  #_=\u003e                [3 1] :nice))\n#'user/bar\nuser=\u003e (bar 3 3)\n\nIllegalArgumentException No matching clause: 3 3  user/fn--2410 (NO_SOURCE_FILE:2)\nuser=\u003e (defined-at? bar 3 3)\nfalse\nuser=\u003e Bye for now!%\n```\n\n## Inputs welcome!\n\nAny sort of feedback, code review, pull requests are most welcome!\n\n## License\n\nCopyright © 2014 Rahul Goma Phulore.\n\nDistributed under the Eclipse Public License, the same as Clojure.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmissingfaktor%2Fmatch-block","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmissingfaktor%2Fmatch-block","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmissingfaktor%2Fmatch-block/lists"}