https://bugs.ruby-lang.org/https://bugs.ruby-lang.org/favicon.ico?17113305112022-05-12T19:54:01ZRuby Issue Tracking SystemRuby master - Feature #18773: deconstruct to receive a rangehttps://bugs.ruby-lang.org/issues/18773?journal_id=975752022-05-12T19:54:01Zkddnewton (Kevin Newton)kddnewton@gmail.com
<ul><li><strong>Description</strong> updated (<a title="View differences" href="/journals/97575/diff?detail_id=62495">diff</a>)</li></ul> Ruby master - Feature #18773: deconstruct to receive a rangehttps://bugs.ruby-lang.org/issues/18773?journal_id=979702022-06-14T04:34:27Zmame (Yusuke Endoh)mame@ruby-lang.org
<ul><li><strong>Status</strong> changed from <i>Open</i> to <i>Assigned</i></li><li><strong>Assignee</strong> set to <i>ktsj (Kazuki Tsujimoto)</i></li></ul> Ruby master - Feature #18773: deconstruct to receive a rangehttps://bugs.ruby-lang.org/issues/18773?journal_id=979792022-06-14T07:23:55Zmame (Yusuke Endoh)mame@ruby-lang.org
<ul></ul><p>It would be easier to discuss if you could write a spec of what pattern match will pass what range. I understand as follows by reading your implementation. Right?</p>
<ul>
<li>
<code>ary in [1, 2, 3]</code> will call <code>ary.deconstruct(3..3)</code>, which means "the length must be exactly 3"</li>
<li>
<code>ary in [1, 2, *, 3]</code> will call <code>ary.deconstruct(3..)</code>, which means "the length must be greater than or equal to 3"</li>
</ul>
<p>I understand your motivation, but I wonder if the spec could be more efficient. In the second calling sequence, the match requires only the first, second and last elements, but <code>ary.deconstruct(3..)</code> needs to create an array including all elements because it does not know which elements are required.</p>
<p>Though the current implementation of pattern matching is not so efficient, but I am afraid that the proposed implementation looks very inefficient because it creates a Method object and calls <code>#arity</code>.</p> Ruby master - Feature #18773: deconstruct to receive a rangehttps://bugs.ruby-lang.org/issues/18773?journal_id=981982022-06-24T07:21:56Zktsj (Kazuki Tsujimoto)kazuki@callcc.net
<ul></ul><p>As a designer of pattern matching, I also understand your motivation.<br>
However, I have the following concerns in addition to the ones mame pointed out.</p>
<ul>
<li>In the current implementation, when a pattern match fails in one-line pattern matching, the reason for the failure is displayed as an error message. I think it difficult to do the same thing by your proposal. (This feature is not a must, but it would be nice to have.)</li>
</ul>
<pre><code>$ ruby -e ‘[0] => []’
-e:1:in `<main>’: [0]: [0] length mismatch (given 1, expected 0) (NoMatchingPatternError)
</code></pre>
<ul>
<li>It might have a negative impact on performance. The current implementation caches the return value of <code>deconstruct</code> method, but this approach will no longer be available.</li>
</ul> Ruby master - Feature #18773: deconstruct to receive a rangehttps://bugs.ruby-lang.org/issues/18773?journal_id=982692022-07-03T18:04:56Zkddnewton (Kevin Newton)kddnewton@gmail.com
<ul></ul><p><a class="user active user-mention" href="https://bugs.ruby-lang.org/users/18">@mame (Yusuke Endoh)</a> and <a class="user active user-mention" href="https://bugs.ruby-lang.org/users/3007">@ktsj (Kazuki Tsujimoto)</a> - I definitely understand your concerns! I'm sorry I didn't make it clear in the initial report, I meant to open this ticket as the beginning of a discussion, not as the definitive solution. I would be very open to discussing other options.</p>
<p>Mostly I'm just trying to solve the issue where loading an array with deconstruct is expensive (like deconstruct_keys with the keys argument). I can think of a couple of ways to do this, including the approach in this commit:</p>
<ul>
<li>Deprecate deconstruct without an argument, eventually pass it a range that functions as <a class="user active user-mention" href="https://bugs.ruby-lang.org/users/18">@mame (Yusuke Endoh)</a> described</li>
<li>Deprecate deconstruct without an argument, eventually pass it a size for before, a boolean for *, a size for after</li>
<li>Create a new method deconstruct_indices that receives those arguments, call it if it is defined, otherwise call deconstruct if it is defined</li>
<li>Do nothing and not worry about it 😅</li>
</ul>
<p>I think any of these would be okay. I agree with <a class="user active user-mention" href="https://bugs.ruby-lang.org/users/18">@mame (Yusuke Endoh)</a> that creating a method object is probably a bad idea.</p> Ruby master - Feature #18773: deconstruct to receive a rangehttps://bugs.ruby-lang.org/issues/18773?journal_id=982732022-07-04T03:07:08Znevans (Nicholas Evans)
<ul></ul><p>@kddeisz If we want that second option to short-circuit on min size, we need to send <code>min</code> independently of <code>max_potential_pre_args</code> and <code>max_potential_post_args</code>. E.g. we wouldn't want to accidentally return <code>nil</code> for this <em>(edited for clarity)</em>:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># min array pattern args: 5</span>
<span class="c1"># min find pattern args: 1</span>
<span class="k">case</span> <span class="n">obj</span>
<span class="k">in</span> <span class="p">[</span><span class="no">A</span><span class="p">,</span><span class="no">B</span><span class="p">,</span><span class="no">C</span><span class="p">,</span><span class="no">D</span><span class="p">,</span><span class="o">*</span><span class="p">,</span><span class="no">Z</span><span class="p">];</span> <span class="n">etc</span>
<span class="k">in</span> <span class="p">[</span><span class="no">A</span><span class="p">,</span><span class="no">B</span><span class="p">,</span><span class="no">C</span><span class="p">,</span><span class="o">*</span><span class="p">,</span><span class="no">Y</span><span class="p">,</span><span class="no">Z</span><span class="p">];</span> <span class="n">etc</span>
<span class="k">in</span> <span class="p">[</span><span class="o">*</span><span class="p">,</span> <span class="p">{</span><span class="n">find</span><span class="p">:},</span> <span class="o">*</span><span class="p">];</span> <span class="n">etc</span>
<span class="k">end</span>
</code></pre>
<p>And if we want to return the smallest array containing all potentially used args, <code>[pre, *, post]</code> can return <em>only</em> pre and post elements... with a caveat: disambiguating between when the max args comes from a pattern without a rest-arg, but another pattern has a rest arg. E.g. <code>case obj; in [A] then ...; in [a, *] then ... end</code>. In that case, we also need at least one padding value. It will be completely ignored other than its affect on the array's size, so it can be anything, e.g. <code>nil</code> or <code>:deconstruct_padding</code>. We can request that the padding is <em>always</em> added, or we can send a bool when we really need it. I'd favor sending the bool, just to <em>remind</em> people it's needed. I know I'd forget one day.</p>
<p>So I think the following signature would be sufficient for both short-circuiting and fetching everything necessary into the smallest possible array:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">def</span> <span class="nf">deconstruct</span><span class="p">:</span> <span class="p">()</span> <span class="o">-></span> <span class="no">Array</span> <span class="c1"># no min, no max, return everything</span>
<span class="c1"># must return all, but may short-circuit if size<min || max<size</span>
<span class="o">|</span> <span class="p">(</span><span class="no">Integer</span> <span class="n">min</span><span class="p">,</span> <span class="no">Integer</span><span class="p">?</span> <span class="n">max</span><span class="p">)</span> <span class="o">-></span> <span class="no">Array</span><span class="p">?</span>
<span class="c1"># may return array with only pre and post, w/o rest (maybe +1)</span>
<span class="o">|</span> <span class="p">(</span><span class="no">Integer</span> <span class="n">min</span><span class="p">,</span> <span class="n">boolean</span> <span class="n">pad</span><span class="p">,</span> <span class="no">Integer</span> <span class="n">pre</span><span class="p">,</span> <span class="no">Integer</span> <span class="n">post</span><span class="p">)</span> <span class="o">-></span> <span class="no">Array</span><span class="p">?</span>
</code></pre>
<p>And some examples (For simplicity and terseness, let's assume the following alternative patterns could also be used as multiple "case in" clauses. So, e.g. order and identifying the "winner" matters.):</p>
<p><em>EDITED the following examples - because it's confusing and I got them wrong the first time...</em></p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># Padding is unnecessary for disambiguation when:</span>
<span class="c1"># * only one alternative,</span>
<span class="c1"># * or only pre and post args (no rest),</span>
<span class="c1"># * or contains alternatives with both pre and post args,</span>
<span class="c1"># * or contains an unbound rest-arg,</span>
<span class="c1"># * or largest args w/o rest < max pre + max post</span>
<span class="n">obj</span><span class="p">.</span><span class="nf">deconstruct</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="kp">false</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="k">in</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="o">*</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span>
<span class="n">obj</span><span class="p">.</span><span class="nf">deconstruct</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="kp">false</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="k">in</span> <span class="p">[</span><span class="no">A</span><span class="p">,</span> <span class="no">B</span><span class="p">,</span> <span class="no">C</span><span class="p">,</span> <span class="o">*</span><span class="p">]</span> <span class="o">|</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="n">obj</span><span class="p">.</span><span class="nf">deconstruct</span><span class="p">(</span><span class="mi">6</span><span class="p">,</span> <span class="kp">false</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span> <span class="k">in</span> <span class="p">[</span><span class="no">A</span><span class="p">,</span> <span class="no">B</span><span class="p">,</span> <span class="no">C</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="no">X</span><span class="p">,</span> <span class="no">Y</span><span class="p">,</span> <span class="no">Z</span><span class="p">]</span> <span class="o">|</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="n">c</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="n">z</span><span class="p">]</span>
<span class="n">obj</span><span class="p">.</span><span class="nf">deconstruct</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="kp">false</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span> <span class="k">in</span> <span class="p">[</span><span class="no">A</span><span class="p">,</span> <span class="no">B</span><span class="p">,</span> <span class="no">C</span><span class="p">,</span><span class="o">*</span><span class="p">]</span><span class="o">|</span><span class="p">[</span><span class="o">*</span><span class="p">,</span><span class="no">X</span><span class="p">,</span> <span class="no">Y</span><span class="p">,</span> <span class="no">Z</span><span class="p">]</span> <span class="o">|</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="n">c</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="n">z</span><span class="p">]</span>
<span class="n">obj</span><span class="p">.</span><span class="nf">deconstruct</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="kp">false</span><span class="p">,</span> <span class="mi">4</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="no">A</span><span class="p">,</span> <span class="no">B</span><span class="p">,</span> <span class="no">C</span><span class="p">,</span> <span class="no">D</span><span class="p">,</span> <span class="o">*</span><span class="p">]</span> <span class="o">|</span>
<span class="p">[</span><span class="no">A</span><span class="p">,</span> <span class="no">B</span><span class="p">,</span> <span class="no">C</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="no">Z</span><span class="p">]</span> <span class="o">|</span>
<span class="p">[</span><span class="no">A</span><span class="p">,</span> <span class="no">B</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="no">Y</span><span class="p">,</span> <span class="no">Z</span><span class="p">]</span> <span class="o">|</span>
<span class="p">[</span><span class="no">A</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="no">X</span><span class="p">,</span> <span class="no">Y</span><span class="p">,</span> <span class="no">Z</span><span class="p">]</span> <span class="o">|</span>
<span class="p">[</span><span class="o">*</span><span class="p">,</span> <span class="no">W</span><span class="p">,</span> <span class="no">X</span><span class="p">,</span> <span class="no">Y</span><span class="p">,</span> <span class="no">Z</span><span class="p">]</span> <span class="o">|</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="n">c</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">z</span><span class="p">]</span>
<span class="c1"># Padding is needed for disambiguation when:</span>
<span class="c1"># * multiple alternatives,</span>
<span class="c1"># * and at least one unbound rest-arg,</span>
<span class="c1"># * and no bound rest-args (which would need to fetch everything),</span>
<span class="c1"># * and only pre-args or only post-args (not both),</span>
<span class="c1"># * and largest max pre/post args w/ rest <= max args w/o rest</span>
<span class="n">obj</span><span class="p">.</span><span class="nf">deconstruct</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="kp">true</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="k">in</span> <span class="p">[]</span> <span class="o">|</span> <span class="p">[</span><span class="n">person</span><span class="p">]</span> <span class="o">|</span> <span class="p">[</span><span class="n">person</span><span class="p">,</span> <span class="o">*</span><span class="p">]</span>
<span class="n">obj</span><span class="p">.</span><span class="nf">deconstruct</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="kp">true</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">0</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="n">c</span><span class="p">]</span> <span class="o">|</span> <span class="p">[</span><span class="no">A</span><span class="p">,</span> <span class="o">*</span><span class="p">]</span>
<span class="c1"># Always handling "no rest arg" as pre-arg:</span>
<span class="n">obj</span><span class="p">.</span><span class="nf">deconstruct</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="kp">false</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span> <span class="k">in</span> <span class="p">[</span><span class="no">A</span><span class="p">,</span> <span class="no">B</span><span class="p">,</span> <span class="no">C</span><span class="p">]</span> <span class="o">|</span> <span class="p">[</span><span class="o">*</span><span class="p">,</span> <span class="no">X</span><span class="p">,</span> <span class="no">Y</span><span class="p">,</span> <span class="no">Z</span><span class="p">]</span>
<span class="c1"># Consolidating "no rest arg" with post-arg when no pre-args are used:</span>
<span class="n">obj</span><span class="p">.</span><span class="nf">deconstruct</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="kp">true</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span> <span class="k">in</span> <span class="p">[</span><span class="no">A</span><span class="p">,</span> <span class="no">B</span><span class="p">,</span> <span class="no">C</span><span class="p">]</span> <span class="o">|</span> <span class="p">[</span><span class="o">*</span><span class="p">,</span> <span class="no">X</span><span class="p">,</span> <span class="no">Y</span><span class="p">,</span> <span class="no">Z</span><span class="p">]</span>
</code></pre>
<p>I've actually been thinking about this ticket on-and-off for the last week or so...<br>
I wrote a <em>much</em> larger response, and I've been struggling to edit it to something reasonable! :)<br>
Anyway, I came up with a different approach for you to consider, which I'll post next. I hope you'll find it simple, extensible, and very ruby-ish. :)</p> Ruby master - Feature #18773: deconstruct to receive a rangehttps://bugs.ruby-lang.org/issues/18773?journal_id=982742022-07-04T03:21:22Znevans (Nicholas Evans)
<ul></ul><p>So here's another option: allow <code>deconstruct</code> to return an <code>Enumerable</code> (or duck-typed). Personally, I'd <em>much</em> rather write custom optimized versions of enumerable methods than write a complicated optimized <code>deconstruct</code> method. And if the methods to efficiently pattern match don't currently exist on <code>Enumerable</code>, perhaps they should be added? That way improvements to <code>Enumerable</code> can automatically become improvements to pattern matching and vice versa.</p>
<p>I was thinking something along the lines of:</p>
<ul>
<li>v1: short-circuit using <code>enum.size</code> then proceed with <code>enum.to_a</code> as the input</li>
<li>v2: fill input array with only values that can potentially be used: <code>[*enum.first(pre.max), *enum.last(post.max)]</code><br>
<em>(This might require <code>Enumerable#last</code>. But we already have <code>#reverse_each</code>, and other O(n) or O(n lg n) methods, so why not <code>#last</code>?)</em>
</li>
<li>v3: use the enumerator directly (much more complicated than v1 and v2). enables lazy-loaded short-circuiting.
<ul>
<li>enables find patterns to match against lazily fetched elements, and lazy loading for potential <em>but unlikely</em> pre & post args</li>
<li>might require a caching proxy enumerator, or <code>Enumerator#next</code>,<br>
or a new method like <code>split_at(idx) => [Enumerable => head, Enumerable => tail]</code>
</li>
</ul>
</li>
<li>v4: handle unbounded/infinite sequences with <em>lazy-loaded rest vars</em>. This is maybe a crazy thing, but I think it could be very nice: allow <code>stream in [a, b, *rest]</code> to assign an <em>enumerable</em> to <code>rest</code>, which can then be used as a continuation.</li>
</ul> Ruby master - Feature #18773: deconstruct to receive a rangehttps://bugs.ruby-lang.org/issues/18773?journal_id=982772022-07-04T17:38:46Znevans (Nicholas Evans)
<ul></ul><p>Oops, I made some mistakes in my "needs disambiguation" examples. Assuming that <code>pre+post</code> members will always be returned when present, padding is only needed when the largest pattern with no rest arg is equal to <code>pre+post</code>. Which means it isn't necessary if both pre <em>and</em> post exist in any clauses. So:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># needs padding: 6 == 6+0</span>
<span class="c1"># deconstruct(3, true, 6, 0)</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="n">b</span><span class="p">,</span><span class="n">c</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="n">z</span>
<span class="c1"># args:6, rest:false</span>
<span class="k">in</span> <span class="no">A</span><span class="p">,</span><span class="no">B</span><span class="p">,</span><span class="no">C</span><span class="p">,</span><span class="o">*</span>
<span class="c1"># pre: 3, rest:true</span>
<span class="k">in</span> <span class="no">A</span><span class="p">,</span><span class="no">B</span><span class="p">,</span><span class="n">c</span><span class="p">,</span><span class="n">d</span><span class="p">,</span><span class="o">*</span>
<span class="c1"># pre: 4, rest:true</span>
<span class="k">end</span>
<span class="c1"># only post args: depends on whether we consolidate no-rest args with post-args,</span>
<span class="c1"># or simply always count them as pre-args</span>
<span class="c1"># does need padding: 6 == 0+6</span>
<span class="c1"># deconstruct(3, true, 0, 6)</span>
<span class="c1"># padding not needed: 6 < 6+4</span>
<span class="c1"># deconstruct(3, false, 6, 4)</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="n">b</span><span class="p">,</span><span class="n">c</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="n">z</span>
<span class="c1"># args:6, rest:false</span>
<span class="k">in</span> <span class="o">*</span><span class="p">,</span> <span class="no">X</span><span class="p">,</span><span class="no">Y</span><span class="p">,</span><span class="no">Z</span>
<span class="c1"># post:3, rest:true</span>
<span class="k">in</span> <span class="o">*</span><span class="p">,</span> <span class="no">W</span><span class="p">,</span><span class="no">X</span><span class="p">,</span><span class="no">Y</span><span class="p">,</span><span class="no">Z</span>
<span class="c1"># post:4, rest:true</span>
<span class="k">end</span>
<span class="c1"># doesn't need padding.</span>
<span class="c1"># deconstruct(3, false, 6, 3)</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="n">b</span><span class="p">,</span><span class="n">c</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="n">z</span>
<span class="c1"># args:6, rest:false</span>
<span class="k">in</span> <span class="no">A</span><span class="p">,</span><span class="no">B</span><span class="p">,</span><span class="no">C</span><span class="p">,</span><span class="o">*</span>
<span class="c1"># pre: 3, rest:true</span>
<span class="k">in</span> <span class="o">*</span><span class="p">,</span><span class="no">X</span><span class="p">,</span><span class="no">Y</span><span class="p">,</span><span class="no">Z</span>
<span class="c1"># post:3, rest:true</span>
<span class="k">end</span>
</code></pre>
<p>Of course, it would be fine to <em>always</em> send padding whether needed or not.</p>
<p>But it would be simpler to lean on <code>Enumerable</code>. We don't need <code>deconstruct</code> to short-circuit based on size and slice the data; that duplicates logic that's already in the pattern match code. Creating the input array using <code>size</code>, <code>to_a</code>, <code>first</code>, and <code>last</code> gives us everything that <code>deconstruct</code> could.</p>
<p>And theoretically, if/when pattern matching can lazily iterate, an enumerator could be instrumented and feed that into a query optimizer, which might know which args are unlikely and which should be fetched eagerly.</p>
<p>e.g. no use fetching rest and post args if we only rarely need them:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">case</span> <span class="n">data_scope</span>
<span class="k">in</span> <span class="no">Likely</span><span class="p">,</span> <span class="no">ToSucceed</span><span class="p">,</span> <span class="o">*</span>
<span class="c1"># only need first 2</span>
<span class="k">in</span> <span class="no">Unlikely</span><span class="p">,</span> <span class="no">ToSucceed</span><span class="p">,</span> <span class="o">*</span><span class="n">rest</span><span class="p">,</span> <span class="no">Tail</span>
<span class="c1"># fetch tail? fetch everything?</span>
<span class="k">in</span> <span class="no">Rarity</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="n">c</span><span class="p">,</span><span class="n">d</span><span class="p">,</span><span class="n">e</span><span class="p">,</span><span class="n">f</span><span class="p">,</span><span class="n">h</span><span class="p">,</span><span class="n">i</span><span class="p">,</span><span class="n">j</span><span class="p">,</span><span class="n">k</span><span class="p">,</span><span class="n">l</span><span class="p">,</span><span class="n">m</span>
<span class="c1"># maybe don't prefetch all 13?</span>
<span class="k">end</span>
</code></pre> Ruby master - Feature #18773: deconstruct to receive a rangehttps://bugs.ruby-lang.org/issues/18773?journal_id=984412022-07-22T17:48:13Zkddnewton (Kevin Newton)kddnewton@gmail.com
<ul></ul><p><a class="user active user-mention" href="https://bugs.ruby-lang.org/users/33540">@nevans (Nicholas Evans)</a> I think that's a <em>really</em> good idea. It has the advantage of being backward-compatible as well. <a class="user active user-mention" href="https://bugs.ruby-lang.org/users/18">@mame (Yusuke Endoh)</a>, <a class="user active user-mention" href="https://bugs.ruby-lang.org/users/3007">@ktsj (Kazuki Tsujimoto)</a> what do you think about allowing Enumerable to be returned from deconstruct and using that to match instead?</p>