https://redmine.ruby-lang.org/https://redmine.ruby-lang.org/favicon.ico?17113305112015-11-15T21:08:45ZRuby Issue Tracking SystemRuby master - Feature #11690: Update Hash during multiple assignmenthttps://redmine.ruby-lang.org/issues/11690?journal_id=548632015-11-15T21:08:45Zphluid61 (Matthew Kerwin)matthew@kerwin.net.au
<ul></ul><p>On 16/11/2015 2:32 AM, <a href="mailto:6ftdan@gmail.com" class="email">6ftdan@gmail.com</a> wrote:</p>
<blockquote>
<p>This would be most useful in scenarios where a method or proc return<br>
multiple values. When the method returns the values we don't normally know<br>
the key outside where the hash assignment is.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">example</span> <span class="o">=</span> <span class="nb">proc</span> <span class="p">{</span> <span class="p">[{</span><span class="ss">:hi</span> <span class="o">=></span> <span class="ss">:hello</span><span class="p">},</span> <span class="mi">5</span><span class="p">]</span> <span class="p">}</span>
<span class="nb">hash</span> <span class="o">=</span> <span class="p">{}</span>
<span class="c1"># Currently in Ruby with an Unknown key multiple assignment isn't an</span>
</code></pre>
</blockquote>
<p>option</p>
<blockquote>
<p>hash[????], current = example.call</p>
<a name="We-currently-have-to-two-step-it"></a>
<h1 >We currently have to two step it<a href="#We-currently-have-to-two-step-it" class="wiki-anchor">¶</a></h1>
<p>result, current = example.call<br>
hash.update(result)</p>
<pre><code>
But with `Hash#update=` we don't have to know the key.
</code></pre>
</blockquote>
<p>I get the use-case, but I think the understandability of the code starts to<br>
suffer.</p>
<p>What about something completely new but splat-like?</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="nb">hash</span><span class="p">[</span><span class="o">*</span><span class="p">],</span> <span class="n">current</span> <span class="o">=</span> <span class="n">example</span><span class="p">.</span><span class="nf">call</span>
</code></pre>
<p>This is even better when the hash comes last, it looks more like an options<br>
parameter:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">opts</span> <span class="o">=</span> <span class="n">get_default_hash</span>
<span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">opts</span><span class="p">[</span><span class="o">*</span><span class="p">]</span> <span class="o">=</span> <span class="n">example2</span><span class="p">.</span><span class="nf">call</span>
</code></pre>
<p>I would assume this also works in single assignment:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">opts</span> <span class="o">=</span> <span class="n">get_default_hash</span>
<span class="n">opts</span><span class="p">[</span><span class="o">*</span><span class="p">]</span> <span class="o">=</span> <span class="n">get_new_opts</span>
</code></pre> Ruby master - Feature #11690: Update Hash during multiple assignmenthttps://redmine.ruby-lang.org/issues/11690?journal_id=548792015-11-16T14:17:09Zdanielpclark (Daniel P. Clark)6ftdan@gmail.com
<ul></ul><p>I'm not sure the <code>opts[*]</code> is a good idea. You have have current splat behavior with multiple assignment.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">a</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">c</span> <span class="o">=</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="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">]</span>
<span class="n">a</span>
<span class="c1"># => 1</span>
<span class="n">c</span>
<span class="c1"># => 5</span>
</code></pre>
<p>And the <code>opts[*]</code> is unclear about what it is and what it expects. It could be a <code>Proc</code>.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">prc</span> <span class="o">=</span> <span class="nb">proc</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="n">d</span><span class="o">|</span> <span class="n">b</span><span class="o">+</span><span class="n">c</span><span class="o">+</span><span class="n">d</span> <span class="p">}</span>
<span class="c1"># if [*] were implemented</span>
<span class="n">a</span><span class="p">,</span> <span class="n">prc</span><span class="p">[</span><span class="o">*</span><span class="p">],</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="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="mi">5</span><span class="p">]</span>
</code></pre>
<p>As far as I know <strong><code>Hash</code></strong> is the only core object with an <strong><code>update</code></strong> method. And in other cases where <strong><code>update</code></strong> is used, such as in Rails' ActiveRecord, a <strong><code>Hash</code></strong> is the expected input. So when the <strong><code>update</code></strong> method is called it is generally understood to be expecting a <strong><code>Hash</code></strong> object.</p>
<p>Implementing <code>[*]</code> would be much more work that simply implementing <code>Hash#update=</code></p>
<p>I was thinking about suggesting <code>Hash#merge!=</code> as well but Ruby syntax does not allow the equals sign after and exclamation point. So I have chosen to only suggest <code>Hash#update=</code></p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">Hash</span>
<span class="k">def</span> <span class="nf">update</span><span class="o">=</span><span class="p">(</span><span class="n">h</span><span class="p">)</span>
<span class="n">update</span><span class="p">(</span><span class="n">h</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre> Ruby master - Feature #11690: Update Hash during multiple assignmenthttps://redmine.ruby-lang.org/issues/11690?journal_id=548802015-11-16T21:38:21Zphluid61 (Matthew Kerwin)matthew@kerwin.net.au
<ul></ul><p>On 17/11/2015 12:18 AM, <a href="mailto:6ftdan@gmail.com" class="email">6ftdan@gmail.com</a> wrote:</p>
<blockquote>
<p>Issue <a class="issue tracker-2 status-1 priority-4 priority-default" title="Feature: Update Hash during multiple assignment (Open)" href="https://redmine.ruby-lang.org/issues/11690">#11690</a> has been updated by Daniel P. Clark.</p>
<p>I'm not sure the <code>opts[*]</code> is a good idea. You have have current splat<br>
behavior with multiple assignment.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">a</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">c</span> <span class="o">=</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="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">]</span>
<span class="n">a</span>
<span class="c1"># => 1</span>
<span class="n">c</span>
<span class="c1"># => 5</span>
</code></pre>
<p>And the <code>opts[*]</code> is unclear about what it is and what it expects. It<br>
could be a <code>Proc</code>.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">prc</span> <span class="o">=</span> <span class="nb">proc</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="n">d</span><span class="o">|</span> <span class="n">b</span><span class="o">+</span><span class="n">c</span><span class="o">+</span><span class="n">d</span> <span class="p">}</span>
<span class="c1"># if [*] were implemented</span>
<span class="n">a</span><span class="p">,</span> <span class="n">prc</span><span class="p">[</span><span class="o">*</span><span class="p">],</span> <span class="n">c</span> <span class="o">=</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="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">]</span>
</code></pre>
</blockquote>
<p>Good point. I won't suggest anything to do with hash-splatting <code>**</code> ;)</p>
<blockquote>
<p>As far as I know <strong><code>Hash</code></strong> is the only core object with an <strong><code>update</code></strong><br>
method. And in other cases where <strong><code>update</code></strong> is used, such as in Rails'<br>
ActiveRecord, a <strong><code>Hash</code></strong> is the expected input. So when the <strong><code>update</code></strong> method<br>
is called it is generally understood to be expecting a <strong><code>Hash</code></strong> object.</p>
<p>Implementing <code>[*]</code> would be much more work that simply implementing<br>
<code>Hash#update=</code></p>
<p>I was thinking about suggesting <code>Hash#merge!=</code> as well but Ruby syntax<br>
does not allow the equals sign after and exclamation point. So I have<br>
chosen to only suggest <code>Hash#update=</code></p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">Hash</span>
<span class="k">def</span> <span class="nf">update</span><span class="o">=</span><span class="p">(</span><span class="n">h</span><span class="p">)</span>
<span class="n">update</span><span class="p">(</span><span class="n">h</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
</blockquote>
<p>It's still very strange to think of '<code>update</code>' as a property of a hash<br>
object. Without some kind of new syntactic construct, with its own clear<br>
semantics, this seems like a hack and doesn't feel right in the core (to<br>
me; others may disagree.)</p> Ruby master - Feature #11690: Update Hash during multiple assignmenthttps://redmine.ruby-lang.org/issues/11690?journal_id=548812015-11-17T01:29:38Znobu (Nobuyoshi Nakada)nobu@ruby-lang.org
<ul></ul><p>Just an idea.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">ToBeAssigned</span>
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
<span class="vi">@target</span> <span class="o">=</span> <span class="n">obj</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">method_missing</span><span class="p">(</span><span class="n">m</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">)</span>
<span class="vi">@target</span><span class="p">.</span><span class="nf">__send__</span><span class="p">(</span><span class="n">m</span><span class="p">.</span><span class="nf">to_s</span><span class="p">.</span><span class="nf">chomp</span><span class="p">(</span><span class="s1">'='</span><span class="p">),</span> <span class="o">*</span><span class="n">args</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">respond_to_missing?</span><span class="p">(</span><span class="n">m</span><span class="p">)</span>
<span class="vi">@target</span><span class="p">.</span><span class="nf">respond_to?</span><span class="p">(</span><span class="n">m</span><span class="p">.</span><span class="nf">to_s</span><span class="p">.</span><span class="nf">chomp</span><span class="p">(</span><span class="s1">'='</span><span class="p">))</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">module</span> <span class="nn">Kernel</span>
<span class="k">def</span> <span class="nf">to_be_assigned_with</span>
<span class="no">ToBeAssigned</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="nb">self</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">x</span> <span class="o">=</span> <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="n">x</span><span class="p">.</span><span class="nf">to_be_assigned_with</span><span class="p">.</span><span class="nf">update</span><span class="p">,</span> <span class="n">y</span> <span class="p">,</span><span class="n">z</span> <span class="o">=</span> <span class="p">{</span><span class="ss">c: </span><span class="mi">3</span><span class="p">},</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span>
<span class="nb">p</span> <span class="n">x</span>
</code></pre> Ruby master - Feature #11690: Update Hash during multiple assignmenthttps://redmine.ruby-lang.org/issues/11690?journal_id=548822015-11-17T02:10:13Zdanielpclark (Daniel P. Clark)6ftdan@gmail.com
<ul></ul><p>Nobuyoshi Nakada wrote:</p>
<blockquote>
<p>Just an idea.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">x</span><span class="p">.</span><span class="nf">to_be_assigned_with</span><span class="p">.</span><span class="nf">update</span><span class="p">,</span> <span class="n">y</span> <span class="p">,</span><span class="n">z</span> <span class="o">=</span> <span class="p">{</span><span class="ss">c: </span><span class="mi">3</span><span class="p">},</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span>
</code></pre>
</blockquote>
<p>I absolutely love the extensibility of this! In that it can be used on any object to allow multiple assignment with any method. Brilliant Nobuyoshi! Now if only the method name wasn't so long ;-)</p>
<p>Maybe we could replace <code>to_be_assigned_with</code> to <code>slurp</code>, <code>consume</code>, <code>devour</code>, or <code>injest</code> ;-) It properly expresses the additive nature of the action. Oooh! Or even <code>feed_me</code>. eg) <code>x.feed_me.update</code> ... Although I don't think most of those would make it in as an official method in Ruby; they're a bit silly.</p> Ruby master - Feature #11690: Update Hash during multiple assignmenthttps://redmine.ruby-lang.org/issues/11690?journal_id=548862015-11-17T03:06:31Znobu (Nobuyoshi Nakada)nobu@ruby-lang.org
<ul></ul><p>Daniel P. Clark wrote:</p>
<blockquote>
<p>Maybe we could replace <code>to_be_assigned_with</code> to <code>slurp</code>, <code>consume</code>, <code>devour</code>, or <code>injest</code> ;-)</p>
</blockquote>
<p>My dictionary doesn't have the last word, maybe a compound word from <code>inject</code> and <code>ingest</code>?</p> Ruby master - Feature #11690: Update Hash during multiple assignmenthttps://redmine.ruby-lang.org/issues/11690?journal_id=548952015-11-17T11:41:05Zdanielpclark (Daniel P. Clark)6ftdan@gmail.com
<ul></ul><p>You're right! I misspelled it. If I had added a space "in jest" it would be "a joke". <code>ingest</code> is what I had meant to say. I wrote how it is pronounced rather than how it is spelled.</p> Ruby master - Feature #11690: Update Hash during multiple assignmenthttps://redmine.ruby-lang.org/issues/11690?journal_id=549442015-11-18T18:41:09Zdanielpclark (Daniel P. Clark)6ftdan@gmail.com
<ul></ul><p>I was thinking of what word(s) may best clarify this and not conflict with other potential uses. One issue about the word <code>assigned</code> is it seems like <code>self = </code> or <code>replace</code>. I at first was thinking of <code>to_be</code> ... but that's sounds more like a promise of the future and each method would have to end in <code>ed</code> like so <code>x.to_be.updated = a: 1</code>. To avoid using a promise form and complex <code>ed</code> handling I was thinking of just <code>to</code>; <code>x.to.update</code>. But I don't think this is clear enough. So I have found the shortest thing I can think of that matches the statement <code>to_be_assigned_with</code>, and that is <code>apply</code>. It's short, simple, clear, and reveals to anyone that more is being done here.</p>
<p>To copy from Nobuyoshi's idea</p>
<pre><code class="Ruby syntaxhl" data-language="Ruby"><span class="k">class</span> <span class="nc">Applicable</span>
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
<span class="vi">@target</span> <span class="o">=</span> <span class="n">obj</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">method_missing</span><span class="p">(</span><span class="n">m</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">)</span>
<span class="vi">@target</span><span class="p">.</span><span class="nf">__send__</span><span class="p">(</span><span class="n">m</span><span class="p">.</span><span class="nf">to_s</span><span class="p">.</span><span class="nf">chomp</span><span class="p">(</span><span class="s1">'='</span><span class="p">),</span> <span class="o">*</span><span class="n">args</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">respond_to_missing?</span><span class="p">(</span><span class="n">m</span><span class="p">)</span>
<span class="vi">@target</span><span class="p">.</span><span class="nf">respond_to?</span><span class="p">(</span><span class="n">m</span><span class="p">.</span><span class="nf">to_s</span><span class="p">.</span><span class="nf">chomp</span><span class="p">(</span><span class="s1">'='</span><span class="p">))</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">module</span> <span class="nn">Kernel</span>
<span class="k">def</span> <span class="nf">apply</span>
<span class="no">Applicable</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="nb">self</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">x</span> <span class="o">=</span> <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="n">x</span><span class="p">.</span><span class="nf">apply</span><span class="p">.</span><span class="nf">update</span><span class="p">,</span> <span class="n">y</span> <span class="p">,</span><span class="n">z</span> <span class="o">=</span> <span class="p">{</span><span class="ss">c: </span><span class="mi">3</span><span class="p">},</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span>
<span class="nb">p</span> <span class="n">x</span>
</code></pre>
<p>Arguably I think this is just as clear as <code>to_be_assigned_with</code>, and is different enough for people to notice that something else is going on here. That should be enough.</p> Ruby master - Feature #11690: Update Hash during multiple assignmenthttps://redmine.ruby-lang.org/issues/11690?journal_id=549452015-11-18T19:13:12Zdanielpclark (Daniel P. Clark)6ftdan@gmail.com
<ul></ul><p>If we wanted to avoid chomping the equals sign on methods that have the equals already we'd have to write in a check for that. But I'm not sure that would be as performant.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">Applicable</span>
<span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
<span class="vi">@target</span> <span class="o">=</span> <span class="n">obj</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">method_missing</span><span class="p">(</span><span class="n">m</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">)</span>
<span class="n">m</span> <span class="o">=</span> <span class="vi">@target</span><span class="p">.</span><span class="nf">respond_to?</span><span class="p">(</span><span class="n">m</span><span class="p">)</span> <span class="p">?</span> <span class="n">m</span> <span class="p">:</span> <span class="n">m</span><span class="p">.</span><span class="nf">to_s</span><span class="p">.</span><span class="nf">chomp</span><span class="p">(</span><span class="s1">'='</span><span class="p">)</span>
<span class="vi">@target</span><span class="p">.</span><span class="nf">__send__</span><span class="p">(</span><span class="n">m</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">respond_to_missing?</span><span class="p">(</span><span class="n">m</span><span class="p">)</span>
<span class="vi">@target</span><span class="p">.</span><span class="nf">respond_to?</span><span class="p">(</span><span class="n">m</span><span class="p">.</span><span class="nf">to_s</span><span class="p">.</span><span class="nf">chomp</span><span class="p">(</span><span class="s1">'='</span><span class="p">))</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">module</span> <span class="nn">Kernel</span>
<span class="k">def</span> <span class="nf">apply</span>
<span class="no">Applicable</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="nb">self</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">x</span> <span class="o">=</span> <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="n">x</span><span class="p">.</span><span class="nf">apply</span><span class="p">.</span><span class="nf">update</span><span class="p">,</span> <span class="n">y</span> <span class="p">,</span><span class="n">z</span> <span class="o">=</span> <span class="p">{</span><span class="ss">c: </span><span class="mi">3</span><span class="p">},</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">7</span>
<span class="n">x</span>
<span class="c1"># => {:a=>1, :b=>2, :c=>3}</span>
<span class="c1"># Hash still responds to :[]= method now after :apply</span>
<span class="n">x</span><span class="p">.</span><span class="nf">apply</span><span class="p">[</span><span class="ss">:z</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">x</span>
<span class="c1"># => {:a=>1, :b=>2, :c=>3, :z=>0}</span>
</code></pre>
<p>I think the performance isn't an issue for this change. Anyone who uses the <code>apply</code> method should expect the added behaviors performance hit.</p> Ruby master - Feature #11690: Update Hash during multiple assignmenthttps://redmine.ruby-lang.org/issues/11690?journal_id=550352015-11-22T23:29:39Zdanielpclark (Daniel P. Clark)6ftdan@gmail.com
<ul></ul><p>Even shorter <code>Kernel#with</code></p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">my_hash</span> <span class="o">=</span> <span class="p">{}</span>
<span class="n">my_hash</span><span class="p">.</span><span class="nf">with</span><span class="p">.</span><span class="nf">update</span><span class="p">,</span> <span class="n">b</span><span class="p">,</span> <span class="n">c</span> <span class="o">=</span> <span class="o">-></span><span class="p">{</span> <span class="p">{</span><span class="ss">a: </span><span class="mi">1</span><span class="p">},</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">6</span> <span class="p">}.</span><span class="nf">call</span>
</code></pre> Ruby master - Feature #11690: Update Hash during multiple assignmenthttps://redmine.ruby-lang.org/issues/11690?journal_id=554422015-12-10T12:26:47Zdanielpclark (Daniel P. Clark)6ftdan@gmail.com
<ul></ul><p>I made a gem for this. <a href="https://github.com/danielpclark/assign" class="external">gem assign</a></p>
<p>I figure Kernel#assign is the best method name for this and the object it returns is an Assignable object.</p>