https://bugs.ruby-lang.org/https://bugs.ruby-lang.org/favicon.ico?17113305112013-09-11T18:16:38ZRuby Issue Tracking SystemRuby master - Feature #8895: Destructuring Assignment for Hashhttps://bugs.ruby-lang.org/issues/8895?journal_id=417382013-09-11T18:16:38Zwhitequark (whitequark *)whitequark@whitequark.org
<ul></ul><p>This is an awesome idea! However, the parser bit is really evil. I tried implementing it myself (quite a bit of time ago) and completely gave up. It's not above my comprehension, but the amount of work even for my Ruby parser port is huge and daunting. Doing it in C is a nightmare.</p>
<p>That being said, I'm willing to discuss and/or provide guidance to any interested parties.</p> Ruby master - Feature #8895: Destructuring Assignment for Hashhttps://bugs.ruby-lang.org/issues/8895?journal_id=417582013-09-12T02:41:17Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<ul></ul><p>I suggested something similar in <a href="/issues/5474">[ruby-core:41772]</a>.<br>
Here is a summary from my similar suggestion made in <a href="/issues/5474">[ruby-core:41772]</a>:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="p">{</span><span class="ss">key: </span><span class="s1">'default'</span><span class="p">,</span> <span class="n">other_key</span><span class="p">:,</span> <span class="o">**</span><span class="n">other_options</span><span class="p">}</span> <span class="o">=</span> <span class="p">{</span><span class="ss">other_key: </span><span class="mi">42</span><span class="p">,</span> <span class="ss">foo: </span><span class="s1">'bar'</span><span class="p">}</span>
<span class="n">key</span> <span class="c1"># => 'default'</span>
<span class="n">other_key</span> <span class="c1"># => 42</span>
<span class="n">other_options</span> <span class="c1"># => {foo: 'bar'}</span>
</code></pre>
<p>You'll note that it doesn't give the possibility to map the key to a different variable. Indeed, I don't think that it would be useful and I would rather encourage rubyists to use meaningful option and variable names. It also makes very similar to the way we declare keyword arguments for methods, so no additional learning curve. Your proposal is quite different.</p> Ruby master - Feature #8895: Destructuring Assignment for Hashhttps://bugs.ruby-lang.org/issues/8895?journal_id=417592013-09-12T03:21:56Zsawa (Tsuyoshi Sawada)
<ul></ul><p>Given that destructive assignments with array prohibits the <code>[ ]</code> on the left side of the assignment, that is:</p>
<pre><code>a, b = [1, 2]
</code></pre>
<p>instead of:</p>
<pre><code>[a, b] = [1, 2]
</code></pre>
<p>it would be more consistent if your proposal were:</p>
<pre><code>name: name, age: age = {name: "John Smith", age: 42}
</code></pre>
<p>rather than:</p>
<pre><code>{name: name, age: age} = {name: "John Smith", age: 42}
</code></pre> Ruby master - Feature #8895: Destructuring Assignment for Hashhttps://bugs.ruby-lang.org/issues/8895?journal_id=417622013-09-12T09:52:54Zchendo (Jack Chen)ruby-lang@chen.do
<ul></ul><p>marcandre (Marc-Andre Lafortune) wrote:</p>
<blockquote>
<p>I suggested something similar in <a href="/issues/5474">[ruby-core:41772]</a>.<br>
Here is a summary from my similar suggestion made in <a href="/issues/5474">[ruby-core:41772]</a>:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="p">{</span><span class="ss">key: </span><span class="s1">'default'</span><span class="p">,</span> <span class="n">other_key</span><span class="p">:,</span> <span class="o">**</span><span class="n">other_options</span><span class="p">}</span> <span class="o">=</span> <span class="p">{</span><span class="ss">other_key: </span><span class="mi">42</span><span class="p">,</span> <span class="ss">foo: </span><span class="s1">'bar'</span><span class="p">}</span>
<span class="n">key</span> <span class="c1"># => 'default'</span>
<span class="n">other_key</span> <span class="c1"># => 42</span>
<span class="n">other_options</span> <span class="c1"># => {foo: 'bar'}</span>
</code></pre>
<p>You'll note that it doesn't give the possibility to map the key to a different variable. Indeed, I don't think that it would be useful and I would rather encourage rubyists to use meaningful option and variable names. It also makes very similar to the way we declare keyword arguments for methods, so no additional learning curve. Your proposal is quite different.</p>
</blockquote>
<p>I considered the case of default options, but I couldn't figure out a way to make it read well, and there are many cases where the keys in the hash are not symbols. No value variable after <code>other_key:</code> feels a bit off to me, too.</p>
<p>I'm all for a way to figure out how to get the use case of default options in somehow but I feel that needs more consideration where as this is useful by itself.</p> Ruby master - Feature #8895: Destructuring Assignment for Hashhttps://bugs.ruby-lang.org/issues/8895?journal_id=417632013-09-12T09:54:44Zchendo (Jack Chen)ruby-lang@chen.do
<ul></ul><p>sawa (Tsuyoshi Sawada) wrote:</p>
<blockquote>
<p>Given that destructive assignments with array prohibits the <code>[ ]</code> on the left side of the assignment, that is:</p>
<pre><code>a, b = [1, 2]
</code></pre>
<p>instead of:</p>
<pre><code>[a, b] = [1, 2]
</code></pre>
<p>it would be more consistent if your proposal were:</p>
<pre><code>name: name, age: age = {name: "John Smith", age: 42}
</code></pre>
<p>rather than:</p>
<pre><code>{name: name, age: age} = {name: "John Smith", age: 42}
</code></pre>
</blockquote>
<p>I left the braces in because I felt it would be easier to parse, however if without braces is doable as well, that would work also. Will update the proposal.</p> Ruby master - Feature #8895: Destructuring Assignment for Hashhttps://bugs.ruby-lang.org/issues/8895?journal_id=417642013-09-12T11:58:52Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<ul></ul><p>chendo (Jack Chen) wrote:</p>
<blockquote>
<p>No value variable after <code>other_key:</code> feels a bit off to me, too.</p>
</blockquote>
<p>Not surprising it feels off today, but you better get used to it because it's coming in 2.1.0: <a href="https://bugs.ruby-lang.org/issues/7701" class="external">https://bugs.ruby-lang.org/issues/7701</a></p> Ruby master - Feature #8895: Destructuring Assignment for Hashhttps://bugs.ruby-lang.org/issues/8895?journal_id=418212013-09-15T15:59:19ZAnonymous
<ul></ul><p><a class="user active user-mention" href="https://bugs.ruby-lang.org/users/7314">@whitequark (whitequark *)</a>: Hi whitequark, you here? Let me raise my commendations to you for your parser gem!</p>
<p>As for the issue at hand, why not just say:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="p">{</span> <span class="ss">name: </span><span class="s2">"JohnSmith"</span><span class="p">,</span> <span class="ss">age: </span><span class="mi">42</span> <span class="p">}</span><span class="o">.!</span>
</code></pre>
<p>and have the assignment done:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="nb">name</span> <span class="o">=</span> <span class="s2">"JohnSmith"</span>
<span class="n">age</span> <span class="o">=</span> <span class="mi">42</span>
</code></pre>
<p>If you want the assignment done to different variables, why not take Rails's <code>Hash#slice</code> one step further:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="p">{</span> <span class="ss">n: </span><span class="s2">"JohnSmith"</span><span class="p">,</span> <span class="ss">a: </span><span class="mi">42</span><span class="p">,</span> <span class="ss">foo: </span><span class="s2">"bar"</span> <span class="p">}.</span><span class="nf">slice</span><span class="p">(</span> <span class="ss">name: :n</span><span class="p">,</span> <span class="ss">age: :a</span> <span class="p">)</span> <span class="c1"># produces { name: "JohnSmith", age: 42 }</span>
</code></pre>
<p>and then</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="p">{</span> <span class="ss">n: </span><span class="s2">"JohnSmith"</span><span class="p">,</span> <span class="ss">a: </span><span class="mi">42</span><span class="p">,</span> <span class="ss">foo: </span><span class="s2">"bar"</span> <span class="p">}.</span><span class="nf">slice</span><span class="p">(</span> <span class="ss">name: :n</span><span class="p">,</span> <span class="ss">age: :a</span> <span class="p">)</span><span class="o">.!</span> <span class="c1"># foo: "bar" is ignored away by the #slice method</span>
</code></pre>
<p>produces the desired assignment:<br>
name = "JohnSmith"<br>
age = 42</p>
<p>I hope that <code>.!</code> syntax proposal doesn't suck too hard! It might be a general way of making objects perform assignments to local variables. I'm concerned about feature creep, though.</p> Ruby master - Feature #8895: Destructuring Assignment for Hashhttps://bugs.ruby-lang.org/issues/8895?journal_id=418232013-09-15T16:06:30ZAnonymous
<ul></ul><p>boris_stitnicky: This sort of feature would be close to impossible to implement in CRuby. I can't speak for JRuby or Rubinius (although I would imagine they're in the same position here) but CRuby relies on being able to statically determine all local variables for a scope ahead of time.</p> Ruby master - Feature #8895: Destructuring Assignment for Hashhttps://bugs.ruby-lang.org/issues/8895?journal_id=418242013-09-15T16:21:37ZAnonymous
<ul></ul><p>@charliesome: I thought myself chendo was stretching it, thanks for making me realize why I felt so. It's all about those famous</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">a</span> <span class="o">=</span> <span class="n">a</span> <span class="c1">#=> nil</span>
</code></pre>
<p>cases :-) But... somehow... sorry for a quiche eater like me to say this... I thought that maybe<br>
being able to statically determine local variables is itself a design smell that might need<br>
to be removed from the language... Sorry again for raising issues.</p> Ruby master - Feature #8895: Destructuring Assignment for Hashhttps://bugs.ruby-lang.org/issues/8895?journal_id=419052013-09-20T21:35:20Zalexeymuranov (Alexey Muranov)
<ul></ul><p>How about this:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><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="n">rest</span><span class="p">,</span> <span class="ss">:a</span> <span class="o">=></span> <span class="n">v1</span><span class="p">,</span> <span class="ss">:b</span> <span class="o">=></span> <span class="n">v2</span><span class="p">,</span> <span class="o">**</span><span class="n">options</span><span class="p">)</span> <span class="o">=</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="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="ss">:a</span> <span class="o">=></span> <span class="ss">:foo</span><span class="p">,</span> <span class="ss">:b</span> <span class="o">=></span> <span class="ss">:bar</span><span class="p">,</span> <span class="ss">:c</span> <span class="o">=></span> <span class="kp">false</span><span class="p">,</span> <span class="ss">:d</span> <span class="o">=></span> <span class="kp">true</span>
<span class="n">x</span> <span class="c1"># => 1</span>
<span class="n">y</span> <span class="c1"># => 2</span>
<span class="n">rest</span> <span class="c1"># => [3, 4]</span>
<span class="n">v1</span> <span class="c1"># => :foo</span>
<span class="n">v2</span> <span class="c1"># => :bar</span>
<span class="n">options</span> <span class="c1"># => {:c=>false, :d=>true}</span>
</code></pre> Ruby master - Feature #8895: Destructuring Assignment for Hashhttps://bugs.ruby-lang.org/issues/8895?journal_id=462472014-04-17T23:25:11Zseanlinsley (Sean Linsley)
<ul></ul><p>This is what I'm imagining:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">a</span><span class="p">,</span> <span class="o">*</span><span class="n">b</span><span class="p">,</span> <span class="n">c</span><span class="p">:,</span> <span class="ss">d: </span><span class="s1">'d'</span><span class="p">,</span> <span class="o">**</span><span class="n">e</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="p">{</span><span class="ss">c: </span><span class="mi">2</span><span class="p">}]</span>
<span class="n">a</span> <span class="o">==</span> <span class="mi">1</span>
<span class="n">b</span> <span class="o">==</span> <span class="p">[]</span>
<span class="n">c</span> <span class="o">==</span> <span class="mi">2</span>
<span class="n">d</span> <span class="o">==</span> <span class="s1">'d'</span>
<span class="n">e</span> <span class="o">==</span> <span class="p">{}</span> <span class="c1"># holds any extras just like `b`</span>
</code></pre>
<p>Where an error would be thrown if the hash didn't have the given key, and no default was provided.</p> Ruby master - Feature #8895: Destructuring Assignment for Hashhttps://bugs.ruby-lang.org/issues/8895?journal_id=476742014-07-10T07:00:28Zko1 (Koichi Sasada)
<ul><li><strong>Description</strong> updated (<a title="View differences" href="/journals/47674/diff?detail_id=34458">diff</a>)</li></ul> Ruby master - Feature #8895: Destructuring Assignment for Hashhttps://bugs.ruby-lang.org/issues/8895?journal_id=476752014-07-10T07:11:35Zko1 (Koichi Sasada)
<ul></ul><p>+1 for this proposal.</p>
<p>I feel it is fine for me:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"> <span class="ss">k1: </span><span class="mi">1</span><span class="p">,</span> <span class="ss">k2: </span><span class="mi">2</span> <span class="o">=</span> <span class="n">h</span>
<span class="n">kr1</span><span class="p">:,</span> <span class="ss">kr2: </span><span class="o">=</span> <span class="n">h</span>
<span class="c1">#=> same as</span>
<span class="n">k1</span> <span class="o">=</span> <span class="n">h</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="ss">:k1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">k2</span> <span class="o">=</span> <span class="n">h</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="ss">:k2</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
<span class="n">kr1</span> <span class="o">=</span> <span class="n">h</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="ss">:k1</span><span class="p">)</span>
<span class="n">kr2</span> <span class="o">=</span> <span class="n">h</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="ss">:k2</span><span class="p">)</span>
<span class="c1"># mixed</span>
<span class="ss">k1: </span><span class="mi">1</span><span class="p">,</span> <span class="ss">k2: </span><span class="mi">2</span><span class="p">,</span> <span class="n">kr1</span><span class="p">:,</span> <span class="ss">kr2: </span><span class="o">=</span> <span class="n">h</span>
<span class="c1"># compile to</span>
<span class="n">k1</span> <span class="o">=</span> <span class="n">h</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="ss">:k1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">k2</span> <span class="o">=</span> <span class="n">h</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="ss">:k2</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
<span class="n">kr1</span> <span class="o">=</span> <span class="n">h</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="ss">:k1</span><span class="p">)</span>
<span class="n">kr2</span> <span class="o">=</span> <span class="n">h</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="ss">:k2</span><span class="p">)</span>
</code></pre>
<p>Problem is what happen when `h' is not a hash object (and doesn't have to_hash method).<br>
Just ignore is one option (what ary assignment do, like "a, b = 1 #=> 1, nil").</p>
<blockquote>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">a</span><span class="p">,</span> <span class="o">*</span><span class="n">b</span><span class="p">,</span> <span class="n">c</span><span class="p">:,</span> <span class="ss">d: </span><span class="s1">'d'</span><span class="p">,</span> <span class="o">**</span><span class="n">e</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="p">{</span><span class="ss">c: </span><span class="mi">2</span><span class="p">}]</span>
</code></pre>
</blockquote>
<p>It should be:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">a</span><span class="p">,</span> <span class="p">(</span><span class="n">c</span><span class="p">:,</span> <span class="ss">d: </span><span class="s1">'d'</span><span class="p">,</span> <span class="o">**</span><span class="n">e</span><span class="p">)</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="p">{</span><span class="n">c</span><span class="p">:</span><span class="mi">2</span><span class="p">}]</span>
</code></pre> Ruby master - Feature #8895: Destructuring Assignment for Hashhttps://bugs.ruby-lang.org/issues/8895?journal_id=477092014-07-11T08:03:13Znobu (Nobuyoshi Nakada)nobu@ruby-lang.org
<ul><li><strong>Related to</strong> <i><a class="issue tracker-1 status-6 priority-4 priority-default closed" href="/issues/10028">Bug #10028</a>: nested rest keyword argument</i> added</li></ul> Ruby master - Feature #8895: Destructuring Assignment for Hashhttps://bugs.ruby-lang.org/issues/8895?journal_id=477482014-07-13T23:05:14Zseanlinsley (Sean Linsley)
<ul></ul><p>Koichi Sasada wrote:</p>
<blockquote>
<p>Problem is what happen when `h' is not a hash object (and doesn't have to_hash method).<br>
Just ignore is one option (what ary assignment do, like "a, b = 1 #=> 1, nil").</p>
<blockquote>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">a</span><span class="p">,</span> <span class="o">*</span><span class="n">b</span><span class="p">,</span> <span class="n">c</span><span class="p">:,</span> <span class="ss">d: </span><span class="s1">'d'</span><span class="p">,</span> <span class="o">**</span><span class="n">e</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="p">{</span><span class="ss">c: </span><span class="mi">2</span><span class="p">}]</span>
</code></pre>
</blockquote>
<p>It should be:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">a</span><span class="p">,</span> <span class="p">(</span><span class="n">c</span><span class="p">:,</span> <span class="ss">d: </span><span class="s1">'d'</span><span class="p">,</span> <span class="o">**</span><span class="n">e</span><span class="p">)</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="p">{</span><span class="n">c</span><span class="p">:</span><span class="mi">2</span><span class="p">}]</span>
</code></pre>
</blockquote>
<p>I don't follow. Can't this assignment behave the same way that method argument destructuring does? This currently works:</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">a</span><span class="p">,</span> <span class="o">*</span><span class="n">b</span><span class="p">,</span> <span class="n">c</span><span class="p">:,</span> <span class="ss">d: </span><span class="s1">'d'</span><span class="p">,</span> <span class="o">**</span><span class="n">e</span><span class="p">)</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="k">end</span>
<span class="n">foo</span> <span class="mi">1</span><span class="p">,</span> <span class="ss">c: </span><span class="mi">2</span>
<span class="c1"># => [1, [], 2, "d", {}]</span>
</code></pre> Ruby master - Feature #8895: Destructuring Assignment for Hashhttps://bugs.ruby-lang.org/issues/8895?journal_id=477612014-07-14T10:02:30Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<ul></ul><p>Sean Linsley wrote:</p>
<blockquote>
<p>I don't follow. Can't this assignment behave the same way that method argument destructuring does?</p>
</blockquote>
<p>I agree. Destructuring should work as method & block passing works (apart from block passing)</p> Ruby master - Feature #8895: Destructuring Assignment for Hashhttps://bugs.ruby-lang.org/issues/8895?journal_id=586832016-05-17T08:18:58Zmatz (Yukihiro Matsumoto)matz@ruby.or.jp
<ul></ul><p>The proposed syntax is much harder to implement than it looks. It conflicts with Hash literals. As a result, humans can be confused as well.</p>
<p>Probably this kind of problems should be addressed by pattern matching, which we are considering to add to Ruby in the future.</p>
<p>Matz.</p> Ruby master - Feature #8895: Destructuring Assignment for Hashhttps://bugs.ruby-lang.org/issues/8895?journal_id=600222016-08-09T17:42:59Zbughit (bug hit)
<ul></ul><p>the closest you can get to hash destructuring is via block params:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="p">{</span><span class="ss">a: </span><span class="mi">1</span><span class="p">,</span> <span class="ss">b: </span><span class="mi">2</span><span class="p">}.</span><span class="nf">tap</span> <span class="k">do</span> <span class="o">|</span><span class="n">a</span><span class="p">:,</span> <span class="n">b</span><span class="ss">:|</span>
<span class="k">end</span>
</code></pre>
<p>but unfortunately this has its own issues (<a class="issue tracker-1 status-6 priority-4 priority-default closed" title="Bug: blocks raise on missing and extra keyword args (Rejected)" href="https://bugs.ruby-lang.org/issues/11048">#11048</a>), it's too strict about missing/extra keys, which doesn't make sense since blocks are intended to be looser with parameter binding.</p> Ruby master - Feature #8895: Destructuring Assignment for Hashhttps://bugs.ruby-lang.org/issues/8895?journal_id=940402021-10-06T20:32:03Zjeremyevans0 (Jeremy Evans)merch-redmine@jeremyevans.net
<ul><li><strong>Is duplicate of</strong> <i><a class="issue tracker-2 status-5 priority-4 priority-default closed" href="/issues/6414">Feature #6414</a>: Destructuring Assignment </i> added</li></ul> Ruby master - Feature #8895: Destructuring Assignment for Hashhttps://bugs.ruby-lang.org/issues/8895?journal_id=940422021-10-06T20:32:09Zjeremyevans0 (Jeremy Evans)merch-redmine@jeremyevans.net
<ul><li><strong>Status</strong> changed from <i>Open</i> to <i>Closed</i></li></ul>