{"id":20704609,"url":"https://github.com/egison/sweet-egison","last_synced_at":"2025-06-23T09:06:10.320Z","repository":{"id":56879590,"uuid":"243209156","full_name":"egison/sweet-egison","owner":"egison","description":"Haskell library for non-deterministic pattern matching","archived":false,"fork":false,"pushed_at":"2022-01-23T05:25:38.000Z","size":834,"stargazers_count":17,"open_issues_count":1,"forks_count":1,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-05-26T08:00:50.368Z","etag":null,"topics":["backtracking","egison","haskell","non-linear-pattern","pattern-matching"],"latest_commit_sha":null,"homepage":"","language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/egison.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}},"created_at":"2020-02-26T08:26:51.000Z","updated_at":"2023-10-18T14:18:11.000Z","dependencies_parsed_at":"2022-08-20T22:31:22.984Z","dependency_job_id":null,"html_url":"https://github.com/egison/sweet-egison","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/egison/sweet-egison","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/egison%2Fsweet-egison","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/egison%2Fsweet-egison/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/egison%2Fsweet-egison/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/egison%2Fsweet-egison/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/egison","download_url":"https://codeload.github.com/egison/sweet-egison/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/egison%2Fsweet-egison/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261449763,"owners_count":23159802,"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":["backtracking","egison","haskell","non-linear-pattern","pattern-matching"],"created_at":"2024-11-17T01:13:06.441Z","updated_at":"2025-06-23T09:06:05.311Z","avatar_url":"https://github.com/egison.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Sweet Egison\n\n[![Actions Status](https://github.com/egison/sweet-egison/workflows/latest/badge.svg)](https://github.com/egison/sweet-egison/actions?workflow=latest)\n[![Actions Status](https://github.com/egison/sweet-egison/workflows/release/badge.svg)](https://github.com/egison/sweet-egison/actions?workflow=release)\n[![Hackage](https://img.shields.io/hackage/v/sweet-egison.svg)](https://hackage.haskell.org/package/sweet-egison)\n[![Hackage Deps](https://img.shields.io/hackage-deps/v/sweet-egison.svg)](http://packdeps.haskellers.com/reverse/sweet-egison)\n\nThe [Sweet Egison](https://hackage.haskell.org/package/sweet-egison) is a shallow embedding implementation of non-linear pattern matching with extensible and polymorphic patterns [1].\nThis library desguars the [Egison](https:///www.egison.org) pattern-match expressions into Haskell programs that use [non-deterministic monads](https://github.com/egison/backtracking).\nThis library provides a base of the pattern-match-oriented (PMO) programming style [2] for Haskell users at a practical level of efficiency.\n\n## Getting started\n\nWe code the equivalent pattern match of `case [1, 2, 3] of x : xs -\u003e (x, xs)` in this library as follows:\n\n```haskell\n\u003e matchAll dfs [1, 2, 3] (List Something) [[mc| $x : $xs -\u003e (x, xs) |]]\n[(1,[2,3])]\n```\n\nHere, we can only observe the small syntactic difference in pattern expressions: the variable bindings are prefixed with `$`. (We'll come back to `List Something` later.)\nYou may notice that `matchAll` returns a list.\nIn our library, pattern matching can return many results.\nSee the following example that doubles all elements in a list:\n\n```haskell\n\u003e take 10 $ matchAll dfs [1 ..] (List Something) [[mc| _ ++ $x : _ -\u003e x * 2 |]]\n[2,4,6,8,10,12,14,16,18,20]\n```\n\n`++` is the *join* operator that decomposes a list into an initial prefix and the remaining suffix.\nWe can implement `map` with pattern matching using this:\n\n```haskell\n\u003e map f xs = matchAll dfs xs (List Something) [[mc| _ ++ $x : _ -\u003e f x |]]\n\u003e map (*2) [1,2,3]\n[2,4,6]\n```\n\nNote that we don't see any recursions or `fold`s in our `map` definition! An intuition of `map` function, that applies the function to all elements, are expressed directly in the pattern expression.\n\n### Matchers\n\nBecause our pattern matching can return many results, we can use it to decompose *non-free data types* such as multisets and sets.\nFor example:\n\n```haskell\n\u003e matchAll dfs [1, 2, 3] (Multiset Something) [[mc| $x : $xs -\u003e (x, xs) |]]\n[(1,[2,3]),(2,[1,3]),(3,[1,2])]\n```\n\nWe use `Multiset Something` instead of `List Something` here to match the target `[1, 2, 3]` as a multiset.\nThese parameters such as `Multiset Something`, `List (List Something)`, and `Something` are called *matchers* and specify pattern-matching methods.\nGiven a matcher `m`, `Multiset m` is a matcher for multisets that matches its elements with `m`.\n`Something` is a matcher that provides simple matching methods for an arbitrary value.\nPattern constructors such as `:` and `++` are overloaded over matchers for collections to archive the ad-hoc polymorphism of patterns.\n\n### Controlling matching strategy\n\nSome pattern matching have infinitely many results and `matchAll bfs` is designed to be able to enumerate all the results.\nFor this purpose, `matchAll bfs` traverses a search tree for pattern matching in the breadth-first order.\nThe following example illustrates this:\n\n```haskell\n\u003e take 10 $ matchAll bfs [1 ..] (Set Something) [[mc| $x : $y : _ -\u003e (x, y) |]]\n[(1,1),(2,1),(1,2),(3,1),(1,3),(2,2),(1,4),(4,1),(1,5),(2,3)]\n```\n\nWe can use the depth-first search with `matchAll dfs`.\n\n```haskell\n\u003e take 10 $ matchAll dfs [1 ..] (Set Something) [[mc| $x : $y : _ -\u003e (x, y) |]]\n[(1,1),(1,2),(1,3),(1,4),(1,5),(1,6),(1,7),(1,8),(1,9),(1,10)]\n```\n\nIn most cases, the depth-first search is faster than the default breadth-first search strategy.\nIt is recommended to always use `matchAll dfs` if it is OK to do so.\n\nWith `matchAll dfs`, we can define an intuitive pattern-matching version of `concat` function on lists.\n\n```haskell\n\u003e concat xs = matchAll dfs xs (List (List Something)) [[mc| _ ++ (_ ++ $x : _) : _ -\u003e x |]]\n\u003e concat [[1,2], [3,4,5]]\n[1,2,3,4,5]\n```\n\n### Non-linear patterns\n\nThe non-linear pattern is another powerful pattern-matching feature.\nIt allows us to refer the value bound to variables appear in the left side of the pattern.\nWe provide a pattern syntax named value patterns in the form of `#e`.\nThe `Eql` matcher enables value patterns to match with targets that are equal to the corresponding expression.\nFor example, the following example enumerates (p, p+2) pairs of primes:\n\n```haskell\n\u003e import Data.Numbers.Primes ( primes )\n\u003e take 10 $ matchAll bfs primes (List Eql) [[mc| _ ++ $p : #(p + 2) : _ -\u003e (p, p+2) |]]\n[(3,5),(5,7),(11,13),(17,19),(29,31),(41,43),(59,61),(71,73),(101,103),(107,109)]\n```\n\nWe can implement a pattern-matching version of set functions such as `member` and `intersect` in a declarative way using non-linear patterns.\nMatch clauses are monoids and can be concatenated using `\u003c\u003e`.\n\n```haskell\n\u003e member x xs = match dfs xs (Multiset Eql) [[mc| #x : _ -\u003e True |], [mc| _ -\u003e False |]]\n\u003e member 1 [3,4,1,4]\nTrue\n\u003e intersect xs ys = matchAll dfs (xs, ys) (Pair (Set Eql) (Set Eql)) [[mc| ($x : _, #x : _) -\u003e x |]]\n\u003e intersect [1,2,3] [4,5,3,2]\n[2,3]\n```\n\n### Further readings\n\nSome practical applications of PMO such as a [SAT solver](https://github.com/egison/sweet-egison/blob/master/example/cdcl.hs) are placed under [example/](https://github.com/egison/sweet-egison/blob/master/example/).\nDetailed information of Egison, the original PMO language implementation, can be found on [https://www.egison.org/](https://www.egison.org/) or in [1].\nYou can learn more about pattern-match-oriented programming style in [2].\n\n\n## Implementation\n\nSweet Egison transform patterns into a program that uses non-deterministic monads.\nOur quasi-quoter `mc` translates match clauses into functions that take a target and return a non-deterministic computation as `MonadPlus`-like monadic expression.\nAs `MonadPlus` can express backtracking computation, we can perform efficient backtracking pattern matching.\nFor example, the match clause `[mc| $x : #(x + 10) : _ -\u003e (x, x + 10) |]` is transformed as follows:\n```haskell\n    \\ (mat_a5sV, tgt_a5sW)\n      -\u003e let (tmpM_a5sX, tmpM_a5sY) = (consM mat_a5sV) tgt_a5sW\n         in\n           ((fromList (((cons (GP, GP)) mat_a5sV) tgt_a5sW))\n              \u003e\u003e=\n                (\\ (tmpT_a5sZ, tmpT_a5t0)\n                   -\u003e let x = tmpT_a5sZ in\n                      let (tmpM_a5t1, tmpM_a5t2) = (consM tmpM_a5sY) tmpT_a5t0\n                      in\n                        ((fromList (((cons (GP, WC)) tmpM_a5sY) tmpT_a5t0))\n                           \u003e\u003e=\n                             (\\ (tmpT_a5t3, tmpT_a5t4)\n                                -\u003e ((fromList ((((value (x + 10)) ()) tmpM_a5t1) tmpT_a5t3))\n                                      \u003e\u003e= (\\ () -\u003e pure (x, x + 10)))))))\n```\nThe infix operators `:` and `++` are synonyms of `cons` and `join`, respectively, and desugared in that way during translation.\n\nThe `matchAll` function is defined as a function that creates and passes the argument for this non-deterministic monads.\n```haskell\nmatchAll strategy target matcher =\n  concatMap (\\b -\u003e toList (strategy (matcher, target) \u003e\u003e= b))\n```\n\nConsequently, the pattern-match expression\n```haskell\nmatchAll dfs [1, 2, 3, 12] (Multiset Eql)\n  [[mc| $x : #(x + 10) : _ -\u003e (x, x + 10) |]]\n-- [(2, 12)]\n```\nis transformed into a program that is equivalent to the following:\n```haskell\nconcatMap (\\b -\u003e toList (dfs (Multiset Eql, [1, 2, 3, 12]) \u003e\u003e= b))\n    [\\ (mat_a5sV, tgt_a5sW)\n       -\u003e let (tmpM_a5sX, tmpM_a5sY) = (consM mat_a5sV) tgt_a5sW\n          in\n            ((fromList (((cons (GP, GP)) mat_a5sV) tgt_a5sW))\n               \u003e\u003e=\n                 (\\ (tmpT_a5sZ, tmpT_a5t0)\n                    -\u003e let x = tmpT_a5sZ in\n                       let (tmpM_a5t1, tmpM_a5t2) = (consM tmpM_a5sY) tmpT_a5t0\n                       in\n                         ((fromList (((cons (GP, WC)) tmpM_a5sY) tmpT_a5t0))\n                            \u003e\u003e=\n                              (\\ (tmpT_a5t3, tmpT_a5t4)\n                                 -\u003e ((fromList ((((value (x + 10)) ()) tmpM_a5t1) tmpT_a5t3))\n                                       \u003e\u003e= (\\ () -\u003e pure (x, x + 10)))))))]\n```\n\n### MiniEgison (Deep Embedding) vs. Sweet Egison (Shallow Embedding)\n\n[miniEgison](https://github.com/egison/egison-haskell) is also a Haskell library that implements Egison pattern matching.\nThe main difference between [miniEgison](https://github.com/egison/egison-haskell) and Sweet Egison is that Sweet Egison translates pattern matching into Haskell control expressions (shallow embedding), whereas [miniEgison](https://github.com/egison/egison-haskell) translates it into Haskell data expressions (deep embedding).\nAs a result, Sweet Egison is faster than miniEgison.\nThe following benchmark is taken using MacBook Pro (2017, 2.3 GHz Intel Core i5).\n\n|              | comb2 (n = 15000) | perm2 (n = 5000) | CDCL (50 vars) |\n|--------------|-------------------|------------------|----------------|\n| miniEgison   | 13.029 sec        | 3.854 sec        | 1.025 sec      |\n| Sweet Egison | 0.303 sec         | 0.462 sec        | 0.097 sec      |\n\nThere is almost no execution performance differences between programs written using list comprehensions and Sweet Egison.\n\n|                     | comb2 (n = 15000) | comb2 (n = 15000) | perm2 (n = 5000) | perm2 (n = 10000) |\n|---------------------|-------------------|-------------------|------------------|-------------------|\n| List Comprehensions | 0.347 sec         | 1.244 sec         | 0.409 sec        | 2.077 sec         |\n| Sweet Egison        | 0.309 sec         | 1.081 sec         | 0.434 sec        | 1.984 sec         |\n\nPrograms used for the above benchmarks are follows:\n* [sample/comb2.hs](https://github.com/egison/sweet-egison/blob/master/sample/comb2.hs)\n* [sample/perm2.hs](https://github.com/egison/sweet-egison/blob/master/sample/perm2.hs)\n* [sample/cdcl.hs](https://github.com/egison/sweet-egison/blob/master/sample/cdcl.hs)\n\n## Bibliography\n\n- [1] Satoshi Egi and Yuichi Nishiwaki: Functional Programming in Pattern-Match-Oriented Programming Style, The Art, Science, and Engineering of Programming, 2020, Vol. 4, Issue 3, Article 7, DOI: 10.22152/programming-journal.org/2020/4/7\n- [2] Satoshi Egi and Yuichi Nishiwaki: Non-linear Pattern Matching with Backtracking for Non-free Data Types, APLAS 2018 - Asian Symposium on Programming Languages and Systems, DOI: 11.1007/978-3-030-02768-1_1\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fegison%2Fsweet-egison","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fegison%2Fsweet-egison","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fegison%2Fsweet-egison/lists"}