https://bugs.ruby-lang.org/https://bugs.ruby-lang.org/favicon.ico?17113305112018-07-15T16:36:35ZRuby Issue Tracking SystemRuby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=729582018-07-15T16:36:35Zshevegen (Robert A. Heiler)shevegen@gmail.com
<ul></ul><p>I have one question:</p>
<ul>
<li>Is the above exclusive for "in" in case, or can it be<br>
combined with "when"?</li>
</ul>
<p>E. g.:</p>
<p>case A[0, 1]<br>
when 3<br>
puts 'bla'<br>
in (A, 1, 1)</p>
<a name="etc"></a>
<h1 >etc<a href="#etc" class="wiki-anchor">¶</a></h1>
<p>?</p> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=729602018-07-15T23:03:34Zktsj (Kazuki Tsujimoto)kazuki@callcc.net
<ul></ul><blockquote>
<p>Is the above exclusive for "in" in case, or can it be combined with "when"?</p>
</blockquote>
<p>The former is right, but I don't have any strong opinion about it.</p>
<p>The reason why I select the former is that "in" is superset of "when".</p> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=729892018-07-18T07:14:00Zmrkn (Kenta Murata)muraken@gmail.com
<ul><li><strong>Related to</strong> <i><a class="issue tracker-2 status-5 priority-4 priority-default closed" href="/issues/14913">Feature #14913</a>: Extend case to match several values at once</i> added</li></ul> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=729912018-07-18T07:14:23Zakr (Akira Tanaka)akr@fsij.org
<ul></ul><p>I expect deconstrocut methods will be defined for core classes if this proposal is accepted.</p>
<p>But I feel the deconstruct method of Struct in the sample code is tricky<br>
because it duplicates values.<br>
(s.deconstruct[0][0] and s.deconstruct[1] has same value)</p>
<pre><code>class Struct
def deconstruct; [self] + values; end
end
</code></pre>
<p>I doubt that the deconstruct method is suitable for<br>
standard definition.</p>
<p>I guess "& pattern", pat & pat & ..., may solve this problem.<br>
("pat1 & pat2 & ..." matches if all patterns (pat1, pat2, ...) matches.)</p> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=729932018-07-18T07:31:52Zshyouhei (Shyouhei Urabe)shyouhei@ruby-lang.org
<ul><li><strong>Related to</strong> deleted (<i><a class="issue tracker-2 status-5 priority-4 priority-default closed" href="/issues/14913">Feature #14913</a>: Extend case to match several values at once</i>)</li></ul> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=730012018-07-18T10:07:34Zshyouhei (Shyouhei Urabe)shyouhei@ruby-lang.org
<ul></ul><p>We had some in-detail discussuion about the possibility of this issue in todays developer meeting. Though it seemed a rough cut that needs more brush-ups, the proposal as a whole got positive reactions. So please continue developing.</p>
<p>Some details the attendees did not like:</p>
<ul>
<li>
<p>Deconstruction seems fragile; For instance the following case statement matches, which is very counter-intuitive.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">def</span> <span class="nf">foo</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
<span class="k">case</span> <span class="n">obj</span>
<span class="k">in</span> <span class="n">a</span><span class="p">,</span> <span class="mi">1</span> <span class="o">=></span> <span class="n">b</span><span class="p">,</span> <span class="n">c</span> <span class="k">then</span>
<span class="k">return</span> <span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">c</span>
<span class="k">else</span>
<span class="nb">abort</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="no">A</span> <span class="o">=</span> <span class="no">Struct</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="ss">:x</span><span class="p">,</span> <span class="ss">:y</span><span class="p">)</span>
<span class="nb">p</span> <span class="n">foo</span><span class="p">(</span><span class="no">A</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">])</span> <span class="c1"># => [A, 1, 2]</span>
</code></pre>
</li>
<li>
<p>There is <code>|</code> operator that is good. But why don't you have counterpart <code>&</code> operator?</p>
</li>
<li>
<p>Pinning operator is necessary. However the proposed syntax do not introduce an <em>operator</em> rather it introduces naming convention into local variable naming. This is no good. We need a real operator for that purpose.</p>
</li>
<li>
<p>One-liner mode seems less needed at the moment. Is it necessary for the first version? We can add this later if a real-world use-case is found that such shorthand is convenient, rather than cryptic.</p>
</li>
<li>
<p>Some attendees do not like that arrays cannot be pattern matched as such.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">case</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="p">[</span><span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">]]</span>
<span class="k">in</span> <span class="p">[</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="p">[</span><span class="mi">3</span><span class="p">,</span> <span class="n">d</span><span class="p">]]</span> <span class="c1"># <- unable to do this</span>
<span class="o">...</span>
<span class="k">end</span>
</code></pre>
</li>
<li>
<p>Should <code>#deconstruct</code> be called over and over again to the same case target? Shouldn't that be cached?</p>
</li>
</ul>
<p>But again, these points are about details. The proposal as a whole seemed roughly okay.</p> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=730522018-07-21T00:34:31Zktsj (Kazuki Tsujimoto)kazuki@callcc.net
<ul></ul><p>Thanks for the feedback.</p>
<blockquote>
<p>But I feel the deconstruct method of Struct in the sample code is tricky<br>
because it duplicates values.</p>
</blockquote>
<blockquote>
<ul>
<li>Deconstruction seems fragile; For instance the following case statement matches, which is very counter-intuitive.</li>
</ul>
</blockquote>
<p>It is trade-off with duck typing.</p>
<p>Consider following case.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">MyA</span>
<span class="k">def</span> <span class="nf">deconstruct</span>
<span class="n">dummy</span> <span class="o">=</span> <span class="no">A</span><span class="p">[</span><span class="kp">nil</span><span class="p">,</span> <span class="kp">nil</span><span class="p">]</span>
<span class="k">return</span> <span class="n">dummy</span><span class="p">,</span> <span class="n">my_x</span><span class="p">,</span> <span class="n">my_y</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">obj</span> <span class="o">=</span> <span class="no">MyA</span><span class="p">.</span><span class="nf">new</span>
<span class="k">case</span> <span class="n">obj</span>
<span class="k">in</span> <span class="no">A</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
<span class="o">...</span>
<span class="k">end</span>
</code></pre>
<p>We can match the pattern even if <code>obj</code> is not an instance of <code>A</code> class.</p>
<blockquote>
<p>I guess "& pattern", pat & pat & ..., may solve this problem.<br>
("pat1 & pat2 & ..." matches if all patterns (pat1, pat2, ...) matches.)</p>
</blockquote>
<blockquote>
<ul>
<li>There is <code>|</code> operator that is good. But why don't you have counterpart <code>&</code> operator?</li>
</ul>
</blockquote>
<p>If <code>&</code> operator is also introduced, I think a user wants to use parenthesis to control precedence of patterns.<br>
It conflicts with syntax of my proposal.</p>
<blockquote>
<ul>
<li>Pinning operator is necessary. However the proposed syntax do not introduce an <em>operator</em> rather it introduces naming convention into local variable naming. This is no good. We need a real operator for that purpose.</li>
</ul>
</blockquote>
<p>Agreed.</p>
<blockquote>
<ul>
<li>One-liner mode seems less needed at the moment. Is it necessary for the first version? We can add this later if a real-world use-case is found that such shorthand is convenient, rather than cryptic.</li>
</ul>
</blockquote>
<p>Agreed.</p>
<blockquote>
<ul>
<li>
<p>Some attendees do not like that arrays cannot be pattern matched as such.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">case</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="p">[</span><span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">]]</span>
<span class="k">in</span> <span class="p">[</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="p">[</span><span class="mi">3</span><span class="p">,</span> <span class="n">d</span><span class="p">]]</span> <span class="c1"># <- unable to do this</span>
<span class="o">...</span>
<span class="k">end</span>
</code></pre>
</li>
</ul>
</blockquote>
<p>I can understand motivation of this request.</p>
<blockquote>
<ul>
<li>Should <code>#deconstruct</code> be called over and over again to the same case target? Shouldn't that be cached?</li>
</ul>
</blockquote>
<p>I think it should be cached.</p> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=730722018-07-23T00:24:40Zshyouhei (Shyouhei Urabe)shyouhei@ruby-lang.org
<ul></ul><p>ktsj (Kazuki Tsujimoto) wrote:</p>
<blockquote>
<blockquote>
<ul>
<li>Deconstruction seems fragile; For instance the following case statement matches, which is very counter-intuitive.</li>
</ul>
</blockquote>
<p>It is trade-off with duck typing.</p>
</blockquote>
<p>Do you really think they are worth trading off?</p>
<p>What is, then, the purpose of pattern matching at the first place?</p>
<p>To me it is very troublesome when <code>case obj in a, b, c then ... end</code> matches something non-Array. That should ruin the whole benefit of pattern matching. Pattens should never match against something you don't want to match.</p> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=730782018-07-23T01:54:37Zakr (Akira Tanaka)akr@fsij.org
<ul></ul><p>ktsj (Kazuki Tsujimoto) wrote:</p>
<blockquote>
<p>Thanks for the feedback.</p>
<blockquote>
<p>But I feel the deconstruct method of Struct in the sample code is tricky<br>
because it duplicates values.</p>
</blockquote>
<blockquote>
<ul>
<li>Deconstruction seems fragile; For instance the following case statement matches, which is very counter-intuitive.</li>
</ul>
</blockquote>
<p>It is trade-off with duck typing.</p>
<p>Consider following case.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">MyA</span>
<span class="k">def</span> <span class="nf">deconstruct</span>
<span class="n">dummy</span> <span class="o">=</span> <span class="no">A</span><span class="p">[</span><span class="kp">nil</span><span class="p">,</span> <span class="kp">nil</span><span class="p">]</span>
<span class="k">return</span> <span class="n">dummy</span><span class="p">,</span> <span class="n">my_x</span><span class="p">,</span> <span class="n">my_y</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">obj</span> <span class="o">=</span> <span class="no">MyA</span><span class="p">.</span><span class="nf">new</span>
<span class="k">case</span> <span class="n">obj</span>
<span class="k">in</span> <span class="no">A</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
<span class="o">...</span>
<span class="k">end</span>
</code></pre>
<p>We can match the pattern even if <code>obj</code> is not an instance of <code>A</code> class.</p>
</blockquote>
<p>Hm. I didn't explan my intent well.</p>
<p>My intent is deconstructing pattern is not well correspondence to<br>
pattern matching of functional languages.</p>
<p>In functional languages, a data type is defined with<br>
constructors and their arguments.</p>
<p>For example list of int can be defined in OCaml as follows.</p>
<pre><code>type intlist =
| Inil
| Icons of int * intlist
</code></pre>
<p>There are two constructors:</p>
<ul>
<li>constructor for empty list, Inil. It has no arguments.</li>
<li>constructor for non-empty list, Icons. It has two arguments: first element and remaining list.</li>
</ul>
<p>We can use pattern matching on the value of a data type.</p>
<p>For example, the length of list of int can be defined as follows.</p>
<pre><code>let rec len il =
match il with
| Inil -> 0
| Icons (_, il1) -> 1 + len il1
</code></pre>
<p>pattern matching distinguish the constructor of a value and<br>
extract arguments of their constructors.</p>
<p>I think Ruby's pattern matching should support this style.</p>
<p>In Ruby, a data type of functional language can be implemented as<br>
multiple classes: one class for one constructor.</p>
<pre><code>class Inil
def initialize()
end
end
class Icons
def initialize(e, il)
@e = e
@il = il
end
end
</code></pre>
<p>(More realistic example, such as AST, may be more interesting.)</p>
<p>So, pattern matching need to distinguish class (correspond to constructor)<br>
AND extract arguments for constructor.</p>
<p>In your proposal, it needs that deconstruct method must be implemented like<br>
your Struct#deconstruct.</p>
<p>This means that, if deconstruct method is not defined like Struct#deconstruct,<br>
Ruby's pattern matching is not usable as pattern matching of functional<br>
languages.</p>
<p>For example, your Array#deconstruct and Hash#deconstruct is not like<br>
Struct#deconstruct.<br>
So, I guess your pattern matching is difficult to use data structures<br>
mixing Array and Hash.<br>
I.e. "distinguish Array and Hash, and extract elements" seems difficult.</p>
<p>I expect Ruby's pattern matching support the programming style of<br>
pattern matching of functional languages.<br>
So, I'm suspicious with the deconstructing pattern of your proposal.</p>
<p>Note that I don't stick to "and" pattern.</p> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=730902018-07-24T04:49:33Zegi (Satoshi Egi)
<ul></ul><p>Let me propose you to import the functions for non-linear pattern matching with backtracking that I have implemented as a Ruby gem in the following repository.</p>
<p><a href="https://github.com/egison/egison-ruby/" class="external">https://github.com/egison/egison-ruby/</a></p>
<p>This pattern-matching system allows programmers to replace the nested for loops and conditional branches into simple pattern-match expressions (Please see README of the above GitHub repository).</p>
<p>It achieved that by fulfilling all the following features.</p>
<ul>
<li>Efficiency of the backtracking algorithm for non-linear patterns</li>
<li>Extensibility of pattern matching</li>
<li>Polymorphisim in patterns</li>
</ul>
<p>There are no other programming languages that support all the above features (especially the first and second features) though many works exist for pattern matching (as listed up in the following link: <a href="https://ghc.haskell.org/trac/ghc/wiki/ViewPatterns" class="external">https://ghc.haskell.org/trac/ghc/wiki/ViewPatterns</a>).<br>
Therefore, if Ruby has this pattern-matching facility, it will be a great advantage even over advanced programming languages with academic background such as Haskell.</p> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=730922018-07-24T04:59:09Zshyouhei (Shyouhei Urabe)shyouhei@ruby-lang.org
<ul></ul><p>egi (Satoshi Egi) wrote:</p>
<blockquote>
<p>Let me propose you to import the functions for non-linear pattern matching with backtracking that I have implemented as a Ruby gem in the following repository.</p>
</blockquote>
<p>Open a new issue for that, please. Don't hijack this thread.</p> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=731662018-07-28T01:14:08Zktsj (Kazuki Tsujimoto)kazuki@callcc.net
<ul></ul><p>shyouhei-san:<br>
I changed my mind. We should be able to avoid such "fragile" case.<br>
Though duck typing is important, it should be designed by another approach.</p>
<p>akr-san:</p>
<blockquote>
<p>I think Ruby's pattern matching should support this style.</p>
</blockquote>
<p>I agree, but it isn't enough.<br>
I expect that Ruby's pattern matching also lets us write following code.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">module</span> <span class="nn">URI</span>
<span class="k">def</span> <span class="nf">deconstruct</span>
<span class="p">{</span><span class="ss">scheme: </span><span class="n">scheme</span><span class="p">,</span> <span class="ss">host: </span><span class="n">host</span><span class="p">,</span> <span class="o">...</span><span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">case</span> <span class="no">URI</span><span class="p">(</span><span class="s1">'http://example.com'</span><span class="p">)</span>
<span class="k">in</span> <span class="ss">scheme: </span><span class="s1">'http'</span><span class="p">,</span> <span class="ss">host:
</span><span class="o">...</span>
<span class="k">end</span>
</code></pre>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">Cell</span>
<span class="k">def</span> <span class="nf">deconstruct</span>
<span class="vi">@cdr</span> <span class="p">?</span> <span class="p">[</span><span class="vi">@car</span><span class="p">]</span> <span class="o">+</span> <span class="vi">@cdr</span><span class="p">.</span><span class="nf">deconstruct</span> <span class="p">:</span> <span class="p">[</span><span class="vi">@car</span><span class="p">]</span>
<span class="k">end</span>
<span class="o">...</span>
<span class="k">end</span>
<span class="n">list</span> <span class="o">=</span> <span class="no">Cell</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="no">Cell</span><span class="p">[</span><span class="mi">2</span><span class="p">,</span> <span class="no">Cell</span><span class="p">[</span><span class="mi">3</span><span class="p">,</span> <span class="kp">nil</span><span class="p">]]]</span>
<span class="k">case</span> <span class="n">list</span>
<span class="k">in</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span>
<span class="o">...</span>
<span class="k">end</span>
</code></pre>
<hr>
<p>So, how about an idea which divides deconstructing pattern into typed and non-typed one?</p>
<pre><code>pat:: pat, ... # (Non-typed) deconstructing pattern
val(pat, ...) # Typed deconstructing pattern. It matches an object such that `obj.kind_of?(val)` and `pat, ...` matches `obj.deconstruct`
[pat, ...] # Syntactic sugar of `Array(pat, ...)`. (if needed)
{id: pat, ...} # Syntactic sugar of `Hash(id: pat, ...)`. (if needed)
</code></pre>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">Struct</span>
<span class="k">alias</span> <span class="n">deconstruct</span> <span class="n">values</span>
<span class="k">end</span>
<span class="no">A</span> <span class="o">=</span> <span class="no">Struct</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="ss">:a</span><span class="p">,</span> <span class="ss">:b</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">m</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
<span class="k">case</span> <span class="n">obj</span>
<span class="k">in</span> <span class="no">A</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span>
<span class="ss">:first</span>
<span class="k">in</span> <span class="n">a</span><span class="p">,</span> <span class="n">b</span>
<span class="ss">:second</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">m</span><span class="p">(</span><span class="no">A</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">])</span> <span class="c1">#=> :first</span>
<span class="n">m</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">])</span> <span class="c1">#=> :second</span>
<span class="n">m</span><span class="p">([</span><span class="no">A</span><span class="p">[</span><span class="kp">nil</span><span class="p">,</span> <span class="kp">nil</span><span class="p">],</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">])</span> <span class="c1">#=> NoMatchingPatternError</span>
</code></pre> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=732622018-08-01T00:18:41Zbaweaver (Brandon Weaver)keystonelemur@gmail.com
<ul></ul><p>There was a previous discussion on this which had many good details and discussion:</p>
<p><a href="https://bugs.ruby-lang.org/issues/14709" class="external">https://bugs.ruby-lang.org/issues/14709</a></p>
<p><a class="user active user-mention" href="https://bugs.ruby-lang.org/users/710">@zverok (Victor Shepelev)</a> and I had done some work and writing on this which may yield some new ideas.</p> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=732842018-08-01T14:32:35Zktsj (Kazuki Tsujimoto)kazuki@callcc.net
<ul><li><strong>Related to</strong> <i><a class="issue tracker-2 status-5 priority-4 priority-default closed" href="/issues/14709">Feature #14709</a>: Proper pattern matching</i> added</li></ul> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=735162018-08-12T05:14:55Zbozhidar (Bozhidar Batsov)bozhidar@batsov.com
<ul></ul><p>Btw, won't it better to introduce a new expression named <code>match</code> than to extend <code>case</code>? Seems to me this will make the use of patter matching clearer, and I assume it's also going to simplify the implementation.</p> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=735292018-08-13T12:07:10Zzverok (Victor Shepelev)zverok.offline@gmail.com
<ul></ul><blockquote>
<p>Btw, won't it better to introduce a new expression named <code>match</code> than to extend <code>case</code>?</p>
</blockquote>
<p>I have exactly the opposite question: do we really need <code>in</code>, why not reuse <code>when</code>?.. For all reasonable explanations, case+when IS Ruby's "pattern-matching" (however limited it seems), and I believe introducing new keywords with "similar yet more powerful" behavior will lead to a deep confusion.</p> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=737542018-08-27T18:34:28Zdsisnero (Dominic Sisneros)dsisnero@gmail.com
<ul></ul><p>"The patterns are run in sequence until the first one that matches."</p>
<p>This means O(n) time for matching. If we are adding this with changes to compiler, we should try to compile it to hash lookup O(1) time</p>
<p>Does this allow nesting of patterns Do patterns compose or nest?</p>
<p>def simplify(n)<br>
case n<br>
in IntNode(n)<br>
then n<br>
in NegNode( Negnode n) then<br>
simplify( NegNode.new( simplify (n) )<br>
in AddNode(IntNode(0), right) then<br>
simplify(right)<br>
in MulNode(IntNode(1) right) then<br>
simplify(right)</p>
<p>etc</p>
<p>Java is going to get pattern matching also and has some good ideas</p>
<p><a href="https://www.youtube.com/watchv=n3_8YcYKScw&list=PLX8CzqL3ArzXJ2EGftrmz4SzS6NRr6p2n&index=1&t=907s" class="external">https://www.youtube.com/watchv=n3_8YcYKScw&list=PLX8CzqL3ArzXJ2EGftrmz4SzS6NRr6p2n&index=1&t=907s</a></p>
<p>and</p>
<p><a href="http://cr.openjdk.java.net/~briangoetz/amber/pattern-match.html" class="external">http://cr.openjdk.java.net/~briangoetz/amber/pattern-match.html</a></p>
<p>Their proposal includes nesting and also is O(1) time.</p>
<p>Not sure how much translates though.</p> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=743492018-10-08T16:03:32Zjwmittag (Jörg W Mittag)Ruby-Lang@JoergWMittag.De
<ul></ul><p>I don't have anything specific to say about this particular proposal, I just want to point out that a lot of people have been thinking about how Pattern Matching relates to Object-Oriented Data Abstraction and Dynamic Languages recently. This proposal already mentions Scala and its Extractors, which guarantee that Pattern Matching preserves Abstraction / Encapsulation.</p>
<p>Another language that is semantically even closer to Ruby (highly dynamic, purely OO, Smalltalk heritage) is <a href="http://newspeaklanguage.org/" class="external">Newspeak</a>. The Technical Report <a href="https://www.hpi.uni-potsdam.de/hirschfeld/publications/media/GellerHirschfeldBracha_2010_PatternMatchingForAnObjectOrientedAndDynamicallyTypedProgrammingLanguage_HPI36.pdf" class="external">Pattern Matching for an Object-Oriented and Dynamically Typed Programming Language</a>, which is based on Felix Geller's PhD Thesis, gives a good overview.</p>
<p>Also, influenced by the approach to Pattern Matching in Scala and Newspeak is <a href="http://gracelang.org/" class="external">Grace</a>'s approach, described in <a href="https://pdxscholar.library.pdx.edu/cgi/viewcontent.cgi?article=1108&context=compsci_fac" class="external">Patterns as Objects in Grace</a>.</p> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=776562019-04-17T06:48:12Zktsj (Kazuki Tsujimoto)kazuki@callcc.net
<ul><li><strong>Status</strong> changed from <i>Open</i> to <i>Closed</i></li></ul><p>Applied in changeset trunk|r67586.</p>
<hr>
<p>Introduce pattern matching [EXPERIMENTAL]</p>
<p><a href="/issues/14912">[ruby-core:87945]</a> [Feature <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Introduce pattern matching syntax (Closed)" href="https://bugs.ruby-lang.org/issues/14912">#14912</a>]</p> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=776692019-04-19T05:28:23Zktsj (Kazuki Tsujimoto)kazuki@callcc.net
<ul><li><strong>Status</strong> changed from <i>Closed</i> to <i>Assigned</i></li><li><strong>Assignee</strong> set to <i>ktsj (Kazuki Tsujimoto)</i></li><li><strong>Target version</strong> set to <i>2.7</i></li></ul><p>The specification is still under discussion, so I reopend the issue.</p>
<p>Current specifation summary:</p>
<pre><code>case obj
in pat [if|unless cond]
...
in pat [if|unless cond]
...
else
...
end
pat: var # Variable pattern. It matches any value, and binds the variable name to that value.
| literal # Value pattern. The pattern matches an object such that pattern === object.
| Constant # Ditto.
| ^var # Ditto. It is equivalent to pin operator in Elixir.
| pat | pat | ... # Alternative pattern. The pattern matches if any of pats match.
| pat => var # As pattern. Bind the variable to the value if pat match.
| Constant(pat, ..., *var, pat, ...) # Array pattern. See below for more details.
| Constant[pat, ..., *var, pat, ...] # Ditto.
| [pat, ..., *var, pat, ...] # Ditto (Same as `BasicObject(pat, ...)` ). You can omit brackets (top-level only).
| Constant(id:, id: pat, "id": pat, ..., **var) # Hash pattern. See below for more details.
| Constant[id:, id: pat, "id": pat, ..., **var] # Ditto.
| {id:, id: pat, "id": pat, ..., **var} # Ditto (Same as `BasicObject(id:, ...)` ). You can omit braces (top-level only).
An array pattern matches if:
* Constant === object returns true
* The object has a #deconstruct method that returns Array
* The result of applying the nested pattern to object.deconstruct is true
A hash pattern matches if:
* Constant === object returns true
* The object has a #deconstruct_keys method that returns Hash.
* The result of applying the nested pattern to object.deconstruct_keys(keys) is true
</code></pre>
<p>For more details, please see <a href="https://speakerdeck.com/k_tsj/pattern-matching-new-feature-in-ruby-2-dot-7" class="external">https://speakerdeck.com/k_tsj/pattern-matching-new-feature-in-ruby-2-dot-7</a>.</p> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=778732019-05-01T08:21:05Zduerst (Martin Dürst)duerst@it.aoyama.ac.jp
<ul><li><strong>Has duplicate</strong> <i><a class="issue tracker-2 status-5 priority-4 priority-default closed" href="/issues/15814">Feature #15814</a>: Capturing variable in case-when branches</i> added</li></ul> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=783982019-06-08T11:56:41Zpitr.ch (Petr Chalupa)
<ul></ul><p>Hi, I am really looking forward to this feature. Looks great!</p>
<p>However I'd like to make few suggestions which I believe should be part of the first pattern matching experimental release. I'll include use-cases and try to explain why it would be better to do so.</p>
<a name="1-Pattern-matching-as-first-class-citizen"></a>
<h3 >(1) Pattern matching as first-class citizen<a href="#1-Pattern-matching-as-first-class-citizen" class="wiki-anchor">¶</a></h3>
<p>Everything in Ruby is dynamically accessible (methods, classes, blocks, etc.) so it would be pity if patterns would be an exception from that. There should be an object which will represent the pattern and which can be lifted from the pattern literal.</p>
<p>It may seem that just wrapping the pattern in a lambda as follows is enough to get an object which represents the pattern.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="o">-></span> <span class="n">value</span> <span class="k">do</span>
<span class="k">case</span> <span class="n">value</span>
<span class="k">in</span> <span class="p">(</span><span class="mi">1</span><span class="o">..</span><span class="mi">10</span><span class="p">)</span> <span class="o">=></span> <span class="n">i</span>
<span class="n">do_something_with</span> <span class="n">i</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>In some cases it is sufficient however lets explore some interesting use cases which cannot be implemented without the first-class pattern-matching.</p>
<p><strong>First use-case</strong> to consider is searching for a value in a data structure. Let's assume we have a data-structure (e.g. some in memory database) and we want to provide an API to search for an element with a pattern matching e.g. <code>#search</code>. The structure stores log messages as follows <code>["severity", "message"]</code>. Then something as follows would be desirable.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">def</span> <span class="nf">drain_erros</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="c1"># pops all messages matching at least one pattern</span>
<span class="c1"># and evalueates the appropriate branch with the destructured log message</span>
<span class="c1"># for each matched message</span>
<span class="n">data</span><span class="p">.</span><span class="nf">pop_all</span> <span class="k">case</span>
<span class="k">in</span> <span class="p">[</span><span class="s2">"fatal"</span><span class="p">,</span> <span class="n">message</span><span class="p">]</span>
<span class="n">deal_with_fatal</span> <span class="n">message</span>
<span class="k">in</span> <span class="p">[</span><span class="s2">"error"</span><span class="p">,</span> <span class="n">message</span><span class="p">]</span>
<span class="n">deal_with_error</span> <span class="n">message</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>There are few things to consider. Compared to the already working implementation there is no message given to the case since that will be later provided in the pop_all method. Therefore the case in here has to evaluate to an object which encapsulates the pattern matching allowing to match candidates from the data-structure later in the pop_all implementation. Another important feature is that the object has to allow to match a candidate without immediately evaluating the appropriate branch. It has to give the pop_all method a chance to remove the element from the data-structure first before the arbitrary user code from the branch is evaluated. That is especially important if the data-structure is thread-safe and does locking, then it cannot hold the lock while it runs arbitrary user code. Firstly it limits the concurrency since no other operation can be executed on the data-structure and secondly it can lead to deadlocks since the common recommendation is to never call a user code while still holding an internal lock.</p>
<p>Probably the simplest implementation which would allow the use-case work is to make case in without a message given behave as a syntax sugar for following.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">case</span>
<span class="k">in</span> <span class="p">[</span><span class="sr">/A/</span><span class="p">,</span> <span class="n">b</span><span class="p">]</span>
<span class="n">b</span><span class="p">.</span><span class="nf">succ</span>
<span class="k">end</span>
<span class="c1"># turns into</span>
<span class="o">-></span> <span class="n">value</span> <span class="k">do</span>
<span class="k">case</span> <span class="n">value</span>
<span class="k">in</span> <span class="p">[</span><span class="sr">/A/</span><span class="p">,</span> <span class="n">b</span><span class="p">]</span>
<span class="o">-></span> <span class="p">{</span> <span class="n">b</span><span class="p">.</span><span class="nf">succ</span> <span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>Then the implementation of pop_all could then look as follows.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">def</span> <span class="nf">pop_all</span><span class="p">(</span><span class="n">pattern</span><span class="p">)</span>
<span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">candidate</span><span class="o">|</span>
<span class="c1"># assuming each locks the structure to read the candidate </span>
<span class="c1"># but releases the lock while executing the block which could </span>
<span class="c1"># be arbitrary user code</span>
<span class="n">branch_continuation</span> <span class="o">=</span> <span class="n">pattern</span><span class="p">.</span><span class="nf">call</span><span class="p">(</span><span class="n">candidate</span><span class="p">)</span>
<span class="k">if</span> <span class="n">branch_continuation</span>
<span class="c1"># candidate matched</span>
<span class="n">delete</span> <span class="n">candidate</span> <span class="c1"># takes a lock internally to delete the element</span>
<span class="n">branck_continuation</span><span class="p">.</span><span class="nf">call</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>In this example it never leaks the inner lock.</p>
<p><strong>Second use case</strong> which somewhat expands the first one is to be able to implement <code>receive</code> method of the concurrent abstraction called Actors. (<code>receive</code> blocks until matching message is received.) Let's consider an actor which receives 2 Integers adds them together and then replies to an actor which asks for a result with <code>[:sum, myself]</code> message then it terminates.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">Actor</span><span class="p">.</span><span class="nf">act</span> <span class="k">do</span>
<span class="c1"># block until frist number is received</span>
<span class="n">first</span> <span class="o">=</span> <span class="n">receive</span> <span class="k">case</span>
<span class="k">in</span> <span class="no">Numeric</span> <span class="o">=></span> <span class="n">value</span>
<span class="n">value</span>
<span class="k">end</span>
<span class="c1"># block until second number is received, then add them</span>
<span class="n">sum</span> <span class="o">=</span> <span class="n">first</span> <span class="o">+</span> <span class="n">receive</span> <span class="k">case</span>
<span class="k">in</span> <span class="no">Numeric</span> <span class="o">=></span> <span class="n">value</span>
<span class="n">value</span>
<span class="k">end</span>
<span class="c1"># when a :sum command is received with the sender reference</span>
<span class="c1"># send sum back</span>
<span class="n">receive</span> <span class="k">case</span>
<span class="k">in</span> <span class="p">[</span><span class="ss">:sum</span><span class="p">,</span> <span class="n">sender</span><span class="p">]</span>
<span class="n">sender</span><span class="p">.</span><span class="nf">send</span> <span class="n">sum</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>It would be great if we could use pattern matching for messages as it is used in Erlang and in Elixir.<br>
The receive method as the <code>pop_all</code> method needs to first find the first matching message in the mailbox without running the user code immediately, then it needs to take the matching message from the Actor's mailbox (while locking the mailbox temporarily) before it can be passed to the arbitrary user code in the case branch (without the lock held).</p>
<p>If <code>case in</code> without message is first class it could be useful to also have shortcut to define simple mono patterns.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">case</span>
<span class="k">in</span> <span class="p">[</span><span class="ss">:sum</span><span class="p">,</span> <span class="n">sender</span><span class="p">]</span>
<span class="n">sender</span><span class="p">.</span><span class="nf">send</span> <span class="n">sum</span>
<span class="k">end</span>
<span class="c1"># could be just</span>
<span class="k">in</span> <span class="p">[</span><span class="ss">:sum</span><span class="p">,</span> <span class="n">sender</span><span class="p">]</span> <span class="p">{</span> <span class="n">sender</span><span class="p">.</span><span class="nf">send</span> <span class="n">sum</span> <span class="p">}</span>
</code></pre>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">case</span>
<span class="k">in</span> <span class="p">[</span><span class="s2">"fatal"</span><span class="p">,</span> <span class="n">_</span><span class="p">]</span> <span class="o">-></span> <span class="n">message</span>
<span class="n">message</span>
<span class="k">end</span>
<span class="c1"># could be just, default block being identity function</span>
<span class="k">in</span> <span class="p">[</span><span class="s2">"fatal"</span><span class="p">,</span> <span class="n">_</span><span class="p">]</span>
</code></pre>
<p>Then the Actor example could be written only as follows:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">Actor</span><span class="p">.</span><span class="nf">act</span> <span class="k">do</span>
<span class="c1"># block until frist number is received</span>
<span class="n">first</span> <span class="o">=</span> <span class="n">receive</span> <span class="k">in</span> <span class="no">Numeric</span>
<span class="c1"># block until second number is received, then add them</span>
<span class="n">sum</span> <span class="o">=</span> <span class="n">first</span> <span class="o">+</span> <span class="n">receive</span> <span class="k">in</span> <span class="no">Numeric</span>
<span class="c1"># when a :sum command is received with the sender reference</span>
<span class="c1"># send sum back</span>
<span class="n">receive</span> <span class="k">in</span> <span class="p">[</span><span class="ss">:sum</span><span class="p">,</span> <span class="n">sender</span><span class="p">]</span> <span class="p">{</span> <span class="n">sender</span><span class="p">.</span><span class="nf">send</span> <span class="n">sum</span> <span class="p">}</span>
<span class="k">end</span>
</code></pre>
<a name="2-Matching-of-non-symbol-key-Hashes"></a>
<h3 >(2) Matching of non symbol key Hashes<a href="#2-Matching-of-non-symbol-key-Hashes" class="wiki-anchor">¶</a></h3>
<p>This was already mentioned as one of the problems to be looked at in future in the RubyKaigi's talk. If <code>=></code> is taken for as pattern then it cannot be used to match hashes with non-Symbol keys. I would suggest to use just <code>=</code> instead, so <code>var = pat</code>. Supporting non-Symbol hashes is important for use cases like:</p>
<ol>
<li>Matching data loaded from JSON where keys are strings</li>
</ol>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">case</span> <span class="p">{</span> <span class="s2">"name"</span> <span class="o">=></span> <span class="s2">"Gustav"</span><span class="p">,</span> <span class="o">**</span><span class="n">other_data</span> <span class="p">}</span>
<span class="k">in</span> <span class="s2">"name"</span> <span class="o">=></span> <span class="p">(</span><span class="nb">name</span> <span class="o">=</span> <span class="sr">/^Gu.*/</span><span class="p">),</span> <span class="o">**</span><span class="n">other</span>
<span class="nb">name</span> <span class="c1">#=> "Gustav"</span>
<span class="n">other</span> <span class="c1">#=> other_data</span>
<span class="k">end</span>
</code></pre>
<ol start="2">
<li>Using pattern to match the key</li>
</ol>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># let's assume v1 of a protocol sends massege {foo: data} </span>
<span class="c1"># but v2 sends {FOO: data}, </span>
<span class="c1"># where data stays the same in both versions, </span>
<span class="c1"># then it is desirable to have one not 2 branches</span>
<span class="k">case</span> <span class="n">message_as_hash</span>
<span class="k">in</span> <span class="p">(</span><span class="ss">:foo</span> <span class="o">|</span> <span class="ss">:FOO</span><span class="p">)</span> <span class="o">=></span> <span class="n">data</span>
<span class="n">process</span> <span class="n">data</span>
<span class="k">end</span>
</code></pre>
<p>Could that work or is there a problem with parsing <code>=</code> in the pattern?</p>
<h2>Note about <code>in [:sum, sender] { sender.send sum }</code>
</h2>
<p><code>in [:sum, sender] { sender.send sum }</code> is quite similar to <code>-></code> syntax for lambdas. However in this suggestion above it would be de-sugared to <code>-> value { case value; in [:sum, sender]; -> { sender.send sum }}</code> which is not intuitive. A solution to consider would be to not to de-sugar the branch into another inner lambda but allow to check if an object matches the pattern (basically asking if the partial function represented by the block with a pattern match accepts the object). Then the example of implementing pop_all would look as follows.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">def</span> <span class="nf">pop_all</span><span class="p">(</span><span class="n">pattern</span><span class="p">)</span>
<span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">candidate</span><span class="o">|</span>
<span class="c1"># assuming each locks the structure to read the candidate </span>
<span class="c1"># but releases the lock while executing the block which could </span>
<span class="c1"># be arbitrary user code</span>
<span class="c1"># does not execute the branches only returns true/false</span>
<span class="k">if</span> <span class="n">pattern</span><span class="p">.</span><span class="nf">matches?</span><span class="p">(</span><span class="n">candidate</span><span class="p">)</span>
<span class="c1"># candidate matched</span>
<span class="n">delete</span> <span class="n">candidate</span> <span class="c1"># takes a lock internally to delete the element</span>
<span class="n">pattern</span><span class="p">.</span><span class="nf">call</span> <span class="n">candidate</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>What are your thoughts?<br>
Do you think this could become part of the first pattern matching release?</p> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=784032019-06-09T00:43:25Zmame (Yusuke Endoh)mame@ruby-lang.org
<ul></ul><p><a class="user active user-mention" href="https://bugs.ruby-lang.org/users/8219">@pitr.ch (Petr Chalupa)</a><br>
Please briefly summarize your proposal first. Use case is important, but explaining a proposal by use case is difficult to read (unless the use case is really simple).</p>
<p>I'm unsure but I guess your proposal:</p>
<ol>
<li>Add a syntactic sugar: <code>case in <pattern>; <expr>; ...; end</code> → <code>-> x { case x in <pattern>; <expr>; ...; end }</code>
</li>
<li>Allow hash rocket pattern: <code>{ <pattern> => <pattern> }</code>
</li>
<li>Add a syntactic sugar: <code>in <pattern> { <expr> }</code> → <code>-> x { case x in <pattern>; <expr>; end }</code>
</li>
</ol>
<p>The following is my opinion.</p>
<p>I don't like (1). It would be more readable to write it explicitly:</p>
<pre><code>data.pop_all do |entry|
case entry
in ["fatal", message]
deal_with_fatal message
in ["error", message]
deal_with_error message
end
end
</code></pre>
<p>We need to be careful about (2). If <code>=></code> pattern is allowed, we can write a variable as a key, but it brings ambiguity.</p>
<pre><code>h = { "foo" => 1, "bar" => 1 }
case h
in { x => 1 }
p x #=> "foo"? "bar"?
end
</code></pre>
<p>I think the current design (allowing only symbol keys) is reasonable.</p>
<p>(3) will introduce syntactic ambiguity. Consider the following example.</p>
<pre><code>case x
in 1
{}
in 2
{} # Is this the second pattern? Or is this a lambda?
end
</code></pre>
<p>It looks like this case statement has two clauses, but <code>in 2 {}</code> (a lambda expression you propose) can be also parsed.</p> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=785672019-06-14T12:16:37Zpitr.ch (Petr Chalupa)
<ul></ul><p>I've intentionally focused on use-cases because: I wanted to make clear why Ruby should to support this; and I've included a possible solution only to make it more clear, since I think Kazuki or somebody else who also spend significant time working on this is the best person to figure out how to support it.</p>
<p>Regarding yours comments.</p>
<p>(1) What I am proposing is different in one key aspect, it allows the <code>pop_all</code> method to execute its own code after the pattern is matched but <strong>before</strong> the branch with the user code is executed. Therefore the pattern of only wrapping <code>case in</code> in lambda is not sufficient to implement this.</p>
<p>To clarify I have described two approaches: First <code>case in <pattern>; <expr>; ...; end</code> => <code>-> x { case x in <pattern>; -> { <expr> }; ...; end }</code>. Second is without the inner lambda <code>case in <pattern>; <expr>; ...; end</code> => <code>pm = -> x { case x in <pattern>; <expr>; ...; end }</code> however an object in <code>pm</code> has to have a <code>accepts?(value)</code> method for the <code>pop_all</code> method to be able to execute just the pattern part without the bodies. I think the second is better.</p>
<p>(2) Thanks for pointing out the ambiguity problem. Id like to explore ideas how to deal with the ambiguity in <code>{ x => 1 }</code>:</p>
<ul>
<li>Simply raising error when the hash cannot be matched exactly. Could often fail too surprisingly.</li>
<li>
<code>x</code> would be an array of values. What it should be if the value is only one though? It would produce mixed results unless always checked with <code>in { x => 1 } if Array === x</code>
</li>
<li>Evaluate the branch for each matched x. Combination explosion could be a problem for <code>[{a=>1},{b=>2},...]</code>, would it run the branch for each value combination assigned to <code>a</code> and <code>b</code> variables?</li>
<li>Force users to write <code>{ *x => 1 }</code> when there is possibility of multiple results. Then x would be Array of all the matched keys (could be just one). This is however not that simple: <code>{ (*k = /^pre/) => *v }</code> applied to <code>{'pre-a'=>1, 'pre-b'=>2}</code> could be <code>k=%w[pre-a pre-b]</code> and <code>v=[a,2]</code>; and then even more complicated <code>{ 1..10 => *([_, second]) }</code> applied to <code>{1=>[1,:a],2=>[2,:b]}</code> would produce <code>second=[:a,:b]</code>. Feels like this should be possible to define precisely.</li>
</ul>
<p>(3) Indeed the shortcut I've proposed would be problematic, could it rather be <code>-> in <pattern> {}</code> then?</p> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=785782019-06-14T23:40:25Zktsj (Kazuki Tsujimoto)kazuki@callcc.net
<ul><li><strong>Related to</strong> <i><a class="issue tracker-2 status-5 priority-4 priority-default closed" href="/issues/15865">Feature #15865</a>: `<expr> in <pattern>` expression</i> added</li></ul> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=785802019-06-14T23:40:53Zktsj (Kazuki Tsujimoto)kazuki@callcc.net
<ul><li><strong>Related to</strong> <i><a class="issue tracker-2 status-1 priority-4 priority-default" href="/issues/15918">Feature #15918</a>: Pattern matching for Set</i> added</li></ul> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=785822019-06-14T23:41:12Zktsj (Kazuki Tsujimoto)kazuki@callcc.net
<ul><li><strong>Related to</strong> <i><a class="issue tracker-2 status-1 priority-4 priority-default" href="/issues/15881">Feature #15881</a>: Optimize deconstruct in pattern matching</i> added</li></ul> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=785842019-06-14T23:42:39Zktsj (Kazuki Tsujimoto)kazuki@callcc.net
<ul><li><strong>Related to</strong> <i><a class="issue tracker-2 status-1 priority-4 priority-default" href="/issues/15824">Feature #15824</a>: respond_to pattern for pattern match</i> added</li></ul> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=789012019-06-27T00:20:35Zpvande (Pieter van de Bruggen)
<ul></ul><p>As suggested by Yusuke on Twitter, I'm posting a link to my own personal "wishlist" around pattern matching. I'm happy to discuss any points that might benefit from clarification.</p>
<p><a href="https://gist.github.com/pvande/822a1aba02e5347c39e8e0ac859d752b" class="external">https://gist.github.com/pvande/822a1aba02e5347c39e8e0ac859d752b</a></p> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=825702019-11-08T05:28:14Zktsj (Kazuki Tsujimoto)kazuki@callcc.net
<ul></ul><p><a class="user active user-mention" href="https://bugs.ruby-lang.org/users/8219">@pitr.ch (Petr Chalupa)</a></p>
<p>Matz rejcted your proposal.<br>
Please check <a href="https://bugs.ruby-lang.org/issues/15930" class="external">https://bugs.ruby-lang.org/issues/15930</a> for more details.</p> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=832012019-12-18T03:07:15Ztjpalmer (Tom Palmer)
<ul><li><strong>Subject</strong> changed from <i>Introduce pattern matching syntax</i> to <i>How long experimental?</i></li></ul><p>How long is the current pattern matching expected to stay experimental before becoming stabilized? (And thanks for the work so far on it!)</p> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=832022019-12-18T03:43:55Zalanwu (Alan Wu)
<ul><li><strong>Subject</strong> changed from <i>How long experimental?</i> to <i>Introduce pattern matching syntax</i></li></ul> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=833362019-12-22T22:33:29Zdocx (Lukas Dolezal)
<ul></ul><p>Hi, I was playing with the type/hash patterns (2.7.0-preview3), and I find this confusing and non-intuitive:</p>
<p>while generic hash pattern uses curly brackets e.g.</p>
<p><code>in {id: id, name: "Bob"}</code></p>
<p>when added type to it, it has to be square or round brackets e.g.</p>
<p><code>in User[id: id, name: "Bob"]</code></p>
<p>I wonder what do you think about only allowing curly brackets for hash pattern, and only square brackets for array pattern?</p>
<p>i.e.</p>
<pre><code> ...
| Constant[pat, ..., *var, pat, ...] # Array pattern.
| [pat, ..., *var, pat, ...] # Ditto
| Constant{id:, id: pat, "id": pat, ..., **var} # Hash pattern
| {id:, id: pat, "id": pat, ..., **var} # Ditto
</code></pre>
<p>Thanks</p> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=833372019-12-23T00:21:41Zmame (Yusuke Endoh)mame@ruby-lang.org
<ul></ul><p>docx (Lukas Dolezal) wrote:</p>
<blockquote>
<p>I wonder what do you think about only allowing curly brackets for hash pattern, and only square brackets for array pattern?</p>
</blockquote>
<p>The rationale of the current design I've heard from <a class="user active user-mention" href="https://bugs.ruby-lang.org/users/3007">@ktsj (Kazuki Tsujimoto)</a> is to fit the pattern to the style of its constructor.</p>
<pre><code>Point = Struct.new(:x, :y, :z, keyword_init: true)
pnt = Point[x: 1, y: 2, z: 3]
pnt in Point[x: a, y: b, z: c]
p [a, b, c] #=> [1, 2, 3]
</code></pre>
<p><code>Point[x: 1, y: 2, z: 3]</code> corresponds elegantly to <code>Point[x: a, y: b, z: c]</code>. We cannot write <code>Point{x: 1, y: 2, z: 3}</code>.</p>
<p>But personally, I agree with curly brackets for hash pattern. It is simpler and more intuitive when we focus on the pattern language.</p> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=833822019-12-24T16:51:00Zdocx (Lukas Dolezal)
<ul></ul><p>Thanks <a class="user active user-mention" href="https://bugs.ruby-lang.org/users/18">@mame (Yusuke Endoh)</a> for explanation and response.</p>
<p>That kinda make sense with the <code>Point[]</code>. Is this universal for any class?</p>
<p>I was playing with it and could not make it, for example when I have implemented my own class with <code>deconstruct_keys</code> it breaks the assumption for <code>[]</code>:</p>
<pre><code>class User
attr_accessor :id, :name
def deconstruct_keys(_)
{ id: id, name: name }
end
end
user = User.new
user.id = 7
user.name = "James Bond"
case user
in User[id: 7]
puts "It's James Bond
end
# but I cannot do this anyway
user = User[id: 7]
</code></pre>
<blockquote>
<p>But personally, I agree with curly brackets for hash pattern. It is simpler and more intuitive when we focus on the pattern language.</p>
</blockquote>
<p>So I think we are on the same page :) Will look forward if something will change :) But have no strong opinion, when explained the current implementation makes sense, just it is not immediately obvious (because it is perhaps specific to few specific stdlib classes?)</p> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=833892019-12-25T04:28:43Znaruse (Yui NARUSE)naruse@airemix.jp
<ul><li><strong>Target version</strong> deleted (<del><i>2.7</i></del>)</li></ul> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=833952019-12-25T11:26:21ZEregon (Benoit Daloze)
<ul></ul><p>Actually, <code>in User(id: id, name: "Bob")</code> should work.<br>
<a class="user active user-mention" href="https://bugs.ruby-lang.org/users/14533">@docx (Lukas Dolezal)</a> what do you think of that form?</p>
<p>I would argue that's more similar to both <code>Integer()</code>-like constructors and <code>User.new(id: 1, name: "Bob")</code> constructors.</p>
<p><code>Point[x: 1, y: 2, z: 3]</code> and in general kwargs inside <code>[]</code> look weird to me, I think they are rather uncommon.<br>
<code>Point.new(x: 1, y: 2, z: 3)</code> is a lot more readable IMHO.</p> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=835562019-12-30T03:04:12ZDan0042 (Daniel DeLorme)
<ul></ul><p>mame (Yusuke Endoh) wrote:</p>
<blockquote>
<p><code>Point[x: 1, y: 2, z: 3]</code> corresponds elegantly to <code>Point[x: a, y: b, z: c]</code>. We cannot write <code>Point{x: 1, y: 2, z: 3}</code>.</p>
</blockquote>
<p>I understand the idea but for me this backfired. Because it looks <strong>exactly</strong> like the constructor method but actually means something different, I was completely baffled for several minutes when I saw <code>Integer(..0)</code> in <a class="issue tracker-5 status-5 priority-4 priority-default closed" title="Misc: Which core objects should support deconstruct/deconstruct_keys? (Closed)" href="https://bugs.ruby-lang.org/issues/16464">#16464</a>.</p>
<p>Would it be possible to combine patterns with some kind of "and" operator? In itself I think it would be useful to match <code>str in RX1 & RX2 & RX3</code>, but it would also be possible to write these array/hash patterns as <code>Point&{x: a, y: b, z: c}</code> which I think is close enough to the constructor to be recognizable, but also different enough to tell apart.</p> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=861362020-06-13T00:57:47ZAnonymous
<ul></ul><p>Sorry for necroposting.</p>
<ul>
<li>Instead of <code>in Constant(pattern…)</code> – I agree that it might coïncide with capitalized methods – and <code>in Constant[pattern]</code> – which might coïncide with the syntax sugar for <code>.[](...)</code>, consider <code>when Constant in pattern…</code>.</li>
<li>Similarly, for an empty Hash pattern, instead of implementing <code>in {}</code> as a special case (I might be in the wrong ticket), <code>when {}</code> already does most if not all of the job.</li>
</ul>
<hr>
<ul>
<li>Comment: This holds true for pretty much every deconstructable class:</li>
</ul>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">module</span> <span class="nn">Enumerable</span> <span class="c1"># Not in 2.7.1 somehow</span>
<span class="k">alias</span> <span class="ss">:deconstruct</span> <span class="ss">:to_a</span>
<span class="k">end</span>
</code></pre>
<ul>
<li>Question: Why enforcing <code>NoMatchingPatternError</code>? <em>Enforcing</em> duck-typing?</li>
</ul>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">case</span> <span class="n">arg</span>
<span class="k">in</span> <span class="no">String</span>
<span class="nb">warn</span> <span class="n">deprecated</span>
<span class="err">…</span>
<span class="k">in</span> <span class="no">Numeric</span>
<span class="k">raise</span> <span class="no">NotImplementedError</span><span class="p">,</span> <span class="s1">'to be implemented'</span>
<span class="k">else</span> <span class="k">end</span> <span class="c1"># ← reserved pseudo-keyword?</span>
</code></pre> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=892062020-12-13T13:57:59Zktsj (Kazuki Tsujimoto)kazuki@callcc.net
<ul><li><strong>Related to</strong> <i><a class="issue tracker-2 status-5 priority-4 priority-default closed" href="/issues/17260">Feature #17260</a>: Promote pattern matching to official feature</i> added</li></ul> Ruby master - Feature #14912: Introduce pattern matching syntaxhttps://bugs.ruby-lang.org/issues/14912?journal_id=892082020-12-13T13:58:15Zktsj (Kazuki Tsujimoto)kazuki@callcc.net
<ul><li><strong>Status</strong> changed from <i>Assigned</i> to <i>Closed</i></li></ul>