{"id":20704605,"url":"https://github.com/egison/egison-ruby","last_synced_at":"2025-04-23T01:25:17.070Z","repository":{"id":14304388,"uuid":"17013107","full_name":"egison/egison-ruby","owner":"egison","description":"A Ruby gem for non-linear pattern-matching with backtracking","archived":false,"fork":false,"pushed_at":"2015-12-09T04:19:44.000Z","size":131,"stargazers_count":161,"open_issues_count":0,"forks_count":4,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-03-29T20:51:09.437Z","etag":null,"topics":["pattern-matching"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/egison.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}},"created_at":"2014-02-20T07:58:25.000Z","updated_at":"2024-11-21T19:45:57.000Z","dependencies_parsed_at":"2022-09-09T04:11:33.305Z","dependency_job_id":null,"html_url":"https://github.com/egison/egison-ruby","commit_stats":null,"previous_names":["egisatoshi/egison-ruby"],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/egison%2Fegison-ruby","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/egison%2Fegison-ruby/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/egison%2Fegison-ruby/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/egison%2Fegison-ruby/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/egison","download_url":"https://codeload.github.com/egison/egison-ruby/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250350432,"owners_count":21416125,"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":["pattern-matching"],"created_at":"2024-11-17T01:13:03.814Z","updated_at":"2025-04-23T01:25:17.047Z","avatar_url":"https://github.com/egison.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# The Gem for Egison Pattern Matching\n\nThis Gem provides a way to access non-linear pattern-matching against unfree data types from Ruby.\nWe can directly express pattern-matching against lists, multisets, and sets using this gem.\n\n## Installation\n\n```shell\n$ gem install egison\n```\n\nor\n\n```shell\n$ git clone https://github.com/egison/egison-ruby.git\n$ cd egison-ruby\n$ make\n```\n\nor\n\n```shell\n$ gem install bundler (if you need)\n$ echo \"gem 'egison', :git =\u003e 'https://github.com/egison/egison-ruby.git'\" \u003e Gemfile\n$ bundle install\n```\n\n## Basic Usage\n\nThe library provides `Egison#match_all` and  `Egison#match`.\n\n```Ruby\nrequire 'egison'\n\ninclude Egison\n\nmatch_all(object) do\n  with(pattern) do\n    ...\n  end\nend\n\nmatch(object) do\n  with(pattern) do\n    ...\n  end\n  with(pattern) do\n    ...\n  end\n  ...\nend\n```\n\nIf a pattern matches, a block passed to `with` is called and returns its result.\n\nIn our pattern-matching system, there are cases that pattern-matching has multiple results.\n`match_all` calls the block passed to `with` for each pattern-matching result and returns all results as an array.\n`match_all` takes one single match-clause.\n\nOn the other hand, `match` takes multiple match-clauses.\nIt pattern-matches from the first match-clause.\nIf a pattern matches, it calls the block passed to the matched match-clause and returns a result for the first pattern-matching result.\n\n## Patterns\n\n### Element Patterns and Subcollection Patterns\n\nAn \u003ci\u003eelement pattern\u003c/i\u003e matches the element of the target array.\n\nA \u003ci\u003esubcollection pattern\u003c/i\u003e matches the subcollection of the target array.\nA subcollection pattern has `*` ahead.\n\nA literal that contain `_` ahead is a \u003ci\u003epattern-variable\u003c/i\u003e.\nWe can refer the result of pattern-matching through them.\n\n```Ruby\nmatch_all([1, 2, 3]) do\n  with(List.(*_hs, _x, *_ts)) do\n    [hs, x, ts]\n  end\nend  #=\u003e [[[],1,[2,3]],[[1],2,[3]],[[1,2],3,[]]\n```\n\n### Three Matchers: List, Multiset, Set\n\nWe can write pattern-matching against lists, multisets, and sets.\nWhen we regard an array as a multiset, the order of elements is ignored.\nWhen we regard an array as a set, the duplicates and order of elements are ignored.\n\n`_` is a \u003ci\u003ewildcard\u003c/i\u003e.\nIt matches with any object.\nNote that `__` and `___` are also interpreted as a wildcard.\nThis is because `_` and `__` are system variables and sometimes have its own meaning.\n\n```Ruby\nmatch_all([1, 2, 3]) do\n  with(List.(_a, _b, *_)) do\n    [a, b]\n  end\nend  #=\u003e [[1, 2]]\n\nmatch_all([1, 2, 3]) do\n  with(Multiset.(_a, _b, *_)) do\n    [a, b]\n  end\nend  #=\u003e [[1, 2], [1, 3], [2, 1], [2, 3], [3, 1], [3, 2]]\n\nmatch_all([1, 2, 3]) do\n  with(Set.(_a, _b, *_)) do\n    [a, b]\n  end\nend  #=\u003e [[1, 1],[1, 2],[1, 3],[2, 1],[2, 2],[2, 3],[3, 1],[3, 2],[3, 3]]\n```\n\nNote that `_[]` is provided as syntactic sugar for `List.()`.\n\n```Ruby\nmatch_all([1, 2, 3]) do\n  with(_[_a, _b, *_]) do\n    [a, b]\n  end\nend  #=\u003e [[1, 2]]\n```\n\n### Non-Linear Patterns\n\nNon-linear pattern is the most important feature of our pattern-matching system.\nOur pattern-matching system allows users multiple occurrences of same variables in a pattern.\nA Pattern whose form is `__(\"...\")` is a value pattern.\nIn the place of `...`, we can write any ruby expression we like.\nIt matches the target when the target is equal with the value that `...` evaluated to.\n\n```Ruby\nmatch_all([5, 3, 4, 1, 2]) do\n  with(Multiset.(_a, __(\"a + 1\"), __(\"a + 2\"), *_)) do\n    a\n  end\nend  #=\u003e [1,2,3]\n```\n\nWhen, the expression in the place of `...` is a single variable, we can omit `(\"` and `\")` as follow.\n\n```Ruby\nmatch_all([1, 2, 3, 2, 5]) do\n  with(Multiset.(_a, __a, *_)) do\n    a\n  end\nend  #=\u003e [2,2]\n```\n\n### Pattern Matching against Stream (Infinite List)\n\nWe can do pattern-matching against streams with the `match_stream` expression.\n\n```Ruby\ndef nats\n  (1..Float::INFINITY)\nend\n\nmatch_stream(nats){ with(Multiset.(_m, _n, *_)) { [m, n] } }.take(10)\n#=\u003e[[1, 2], [1, 3], [2, 1], [1, 4], [2, 3], [3, 1], [1, 5], [2, 4], [3, 2], [4, 1]]\n\nmatch_stream(nats){ with(Set.(_m, _n, *_)) { [m, n] } }.take(10)\n#=\u003e[[1, 1], [1, 2], [2, 1], [1, 3], [2, 2], [3, 1], [1, 4], [2, 3], [3, 2], [4, 1]]\n```\n\n## Demonstrations\n\n### Combinations\n\nWe can enumerates all combinations of the elements of a collection with pattern-matching.\n\n```Ruby\nrequire 'egison'\n\ninclude Egison\n\np(match_all([1,2,3,4,5]) do with(List.(*_, _x, *_, _y, *_)) { [x, y] } end)\n#=\u003e [[1, 2], [1, 3], [1, 4], [1, 5], [2, 3], [2, 4], [2, 5], [3, 4], [3, 5], [4, 5]]\n\np(match_all([1,2,3,4,5]) do with(List.(*_, _x, *_, _y, *_, _z, *_)) { [x, y, z] } end)\n#=\u003e [[1, 2, 3], [1, 2, 4], [1, 2, 5], [1, 3, 4], [1, 3, 5], [1, 4, 5], [2, 3, 4], [2, 3, 5], [2, 4, 5], [3, 4, 5]]\n\n```\n\n### Poker Hands\n\nWe can write patterns for all poker-hands in one single pattern.\nIt is as follow.\nIsn't it exciting?\n\n```Ruby\nrequire 'egison'\n\ninclude Egison\n\ndef poker_hands cs\n  match(cs) do\n    with(Multiset.(_[_s, _n], _[__s, __(\"n+1\")], _[__s, __(\"n+2\")], _[__s, __(\"n+3\")], _[__s, __(\"n+4\")])) do\n      \"Straight flush\"\n    end\n    with(Multiset.(_[_, _n], _[_, __n], _[_, __n], _[_, __n], _)) do\n      \"Four of kind\"\n    end\n    with(Multiset.(_[_, _m], _[_, __m], _[_, __m], _[_, _n], _[_, __n])) do\n      \"Full house\"\n    end\n    with(Multiset.(_[_s, _], _[__s, _], _[__s, _], _[__s, _], _[__s, _])) do\n      \"Flush\"\n    end\n    with(Multiset.(_[_, _n], _[_, __(\"n+1\")], _[_, __(\"n+2\")], _[_, __(\"n+3\")], _[_, __(\"n+4\")])) do\n      \"Straight\"\n    end\n    with(Multiset.(_[_, _n], _[_, __n], _[_, __n], _, _)) do\n      \"Three of kind\"\n    end\n    with(Multiset.(_[_, _m], _[_, __m], _[_, _n], _[_, __n], _)) do\n      \"Two pairs\"\n    end\n    with(Multiset.(_[_, _n], _[_, __n], _, _, _)) do\n      \"One pair\"\n    end\n    with(Multiset.(_, _, _, _, _)) do\n      \"Nothing\"\n    end\n  end\nend\n\np(poker_hands([[\"diamond\", 1], [\"diamond\", 3], [\"diamond\", 5], [\"diamond\", 4], [\"diamond\", 2]])) #=\u003e \"Straight flush\"\np(poker_hands([[\"diamond\", 1], [\"club\", 2], [\"club\", 1], [\"heart\", 1], [\"diamond\", 2]])) #=\u003e \"Full house\"\np(poker_hands([[\"diamond\", 4], [\"club\", 2], [\"club\", 5], [\"heart\", 1], [\"diamond\", 3]])) #=\u003e \"Straight\"\np(poker_hands([[\"diamond\", 4], [\"club\", 10], [\"club\", 5], [\"heart\", 1], [\"diamond\", 3]])) #=\u003e \"Nothing\"\n```\n\n### Twin Primes and Prime Triplets\n\nThe following code enumerates all twin primes with pattern-matching!\nI believe it is also a really exciting demonstration.\n\n```Ruby\nrequire 'egison'\nrequire 'prime'\n\ninclude Egison\n\ntwin_primes = match_stream(Prime) {\n  with(List.(*_, _p, __(\"p + 2\"), *_)) {\n    [p, p + 2]\n  }\n}\n\np twin_primes.take(10)\n#=\u003e[[3, 5], [5, 7], [11, 13], [17, 19], [29, 31], [41, 43], [59, 61], [71, 73], [101, 103], [107, 109]]\n```\n\nWe can also enumerate prime triplets using **and-patterns** and **or-patterns** effectively.\n\n```Ruby\nprime_triplets = match_stream(Prime) {\n  with(List.(*_, _p, And(Or(__(\"p + 2\"), __(\"p + 4\")), _m), __(\"p + 6\"), *_)) {\n    [p, m, p + 6]\n  }\n}\n\np prime_triplets.take(10)\n#=\u003e[[5, 7, 11], [7, 11, 13], [11, 13, 17], [13, 17, 19], [17, 19, 23], [37, 41, 43], [41, 43, 47], [67, 71, 73], [97, 101, 103], [101, 103, 107]]\n```\n\n### Algebraic Data Types\n\nOf course, we can also patten match against algebraic data types as ordinary functional programming languages.\nHere is a simple example.\n\n```Ruby\nclass User \u003c Struct.new(:name, :gender, :married, :doctor, :professor)\n  def greet\n    match(self) do\n      with(User.(_name,       _,    _,    _, true)) { \"Hello, Prof. #{name}!\" }\n      with(User.(_name,       _,    _, true,    _)) { \"Hello, Dr. #{name}!\" }\n      with(User.(_name, :female, true,    _,    _)) { \"Hello, Mrs. #{name}!\" }\n      with(User.(_name, :female,    _,    _,    _)) { \"Hello, Ms. #{name}!\" }\n      with(User.(_name,   :male,    _,    _,    _)) { \"Hello, Mr. #{name}!\" }\n      with(User.(_name,       _,    _,    _,    _)) { \"Hello, #{name}!\" }\n    end\n  end\nend\n\nu1 = User.new(\"Egi\", :male, true, false, false)\np(u1.greet)#=\u003e\"Hello, Mr. Egi!\"\n\nu2 = User.new(\"Nanaka\", :girl, false, false, false)\np(u2.greet)#=\u003e\"Hello, Nanaka!\"\n\nu3 = User.new(\"Hirai\", :male, true, true, false)\np(u3.greet)#=\u003e\"Hello, Dr. Hirai!\"\n\nu4 = User.new(\"Hagiya\", :male, true, true, true)\np(u4.greet)#=\u003e\"Hello, Prof. Hagiya!\"\n```\n\nYou can find more demonstrations in the [`sample`](https://github.com/egison/egison-ruby/tree/master/sample) directory.\n\n## About Egison\n\nIf you get to love the above pattern-matching, please try [the Egison programming language](https://github.com/egison/egison), too.\nEgison is the pattern-matching oriented, purely functional programming language.\nActually, the original pattern-matching system of Egison is more powerful.\nFor example, we can do following things in the original Egison.\n\n- We can define new pattern-constructors.\n- We can modularize useful patterns.\n\nThere is a new programming world!\n\n## Contact\n\nIf you get interested in this Gem, please contact \u003ca target=\"_blank\" href=\"http://www.egison.org/~egi/\"\u003eSatoshi Egi\u003c/a\u003e or tweet to \u003ca target=\"_blank\" href=\"https://twitter.com/Egison_Lang\"\u003e@Egison_Lang\u003c/a\u003e.\n\nWe will talk about this gem in \u003ca target=\"_blank\" href=\"http://rubykaigi.org/2014\"\u003eRubyKaigi 2014\u003c/a\u003e!\n\n## LICENSE\n\nThe license of this library code is BSD.\nI learned how to implement pattern-matching in Ruby and how to write a gem from the code of  \u003ca target=\"_blank\" href=\"https://github.com/k-tsj/pattern-match\"\u003ethe pattern-match gem\u003c/a\u003e by Kazuki Tsujimoto.\nI designed syntax of pattern-matching to go with that gem.\nThis library contains the copy from that gem.\nThe full license text is [here](https://github.com/egisatoshi/egison-ruby/blob/master/LICENSE).\n\n## Sponsors\n\nThis project is sponsored by [Rakuten, Inc.](http://global.rakuten.com/corp/) and [Rakuten Institute of Technology](http://rit.rakuten.co.jp/).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fegison%2Fegison-ruby","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fegison%2Fegison-ruby","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fegison%2Fegison-ruby/lists"}