Ruby Issue Tracking System: Issueshttps://bugs.ruby-lang.org/https://bugs.ruby-lang.org/favicon.ico?17113305112024-03-05T15:21:08ZRuby Issue Tracking System
Redmine Ruby master - Bug #20325 (Open): Enumerator.product.size bug with zero * infinite enumeratorshttps://bugs.ruby-lang.org/issues/203252024-03-05T15:21:08Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">Enumerator</span><span class="p">.</span><span class="nf">product</span><span class="p">([],</span> <span class="mi">1</span><span class="o">..</span><span class="p">).</span><span class="nf">to_a</span> <span class="c1"># => [] (OK)</span>
<span class="no">Enumerator</span><span class="p">.</span><span class="nf">product</span><span class="p">([],</span> <span class="mi">1</span><span class="o">..</span><span class="p">).</span><span class="nf">size</span> <span class="c1"># => Infinity (Should be 0)</span>
</code></pre> Ruby master - Bug #18018 (Closed): Float#floor / truncate sometimes result that is too small.https://bugs.ruby-lang.org/issues/180182021-07-01T22:53:33Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="mf">291.4</span><span class="p">.</span><span class="nf">floor</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="c1"># => 291.4 (ok)</span>
<span class="mf">291.4</span><span class="p">.</span><span class="nf">floor</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="c1"># => 291.39 (not ok)</span>
<span class="mf">291.4</span><span class="p">.</span><span class="nf">floor</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="c1"># => 291.4 (ok)</span>
<span class="mf">291.4</span><span class="p">.</span><span class="nf">floor</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span> <span class="c1"># => 291.4 (ok)</span>
<span class="mf">291.4</span><span class="p">.</span><span class="nf">floor</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> <span class="c1"># => 291.39999 (not ok)</span>
<span class="mf">291.4</span><span class="p">.</span><span class="nf">floor</span><span class="p">(</span><span class="mi">6</span><span class="p">)</span> <span class="c1"># => 291.4 (ok)</span>
</code></pre>
<p><code>g = f.floor(n)</code>, for <code>n > 0</code> must return the highest float that has the correct properties:</p>
<ul>
<li>
<code>g</code> <= <code>f</code>
</li>
<li>
<code>g</code>'s decimal string representation has at most <code>n</code> digits</li>
</ul>
<p>I'll note that <code>floor</code> should be stable, i.e. <code>f.floor(n).floor(n) == f.floor(n)</code> for all <code>f</code> and <code>n</code>.</p>
<p>Same idea for <code>truncate</code>, except for negative numbers (where <code>(-f).truncate(n) == -(f.floor(n))</code> for positive <code>f</code>).</p>
<p>Noticed by Eustáquio Rangel but posted on the mailing list.</p>
<p>Please do not reply that I need to learn how floats work. Note that example given in doc <code>(0.3/0.1).floor == 2</code> is not this issue, since <code>0.3/0.1 #=> 2.9999999999999996</code></p> Ruby master - Feature #17838 (Closed): `Set#intersect?` and enumerableshttps://bugs.ruby-lang.org/issues/178382021-04-28T04:49:30Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p><code>Set#intersect?</code> currently accepts only a <code>set</code> argument.</p>
<p>It should accept an enumerable argument:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><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="nf">intersect?</span><span class="p">(</span><span class="no">Set</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="c1"># => true</span>
<span class="no">Set</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="nf">intersection</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="c1"># => Set[2, 3]</span>
<span class="no">Set</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="nf">intersect?</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="c1"># => ArgumentError</span>
</code></pre>
<p>I expect <code>set.intersect?(arg)</code> to be an optimized version of <code>!set.intersection(arg).empty?</code></p>
<p>Should I prepare a PR?</p> Ruby master - Feature #17785 (Open): Allow named parameters to be keywordshttps://bugs.ruby-lang.org/issues/177852021-04-08T15:08:40Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>We should allow named parameters to be keywords and use add a trailing <code>_</code> to the corresponding variable:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">def</span> <span class="nf">check</span><span class="p">(</span><span class="n">arg</span><span class="p">,</span> <span class="k">class</span><span class="p">:)</span>
<span class="n">arg</span><span class="p">.</span><span class="nf">is_a?</span><span class="p">(</span><span class="n">class_</span><span class="p">)</span>
<span class="k">end</span>
<span class="n">check</span><span class="p">(</span><span class="mi">42</span><span class="p">,</span> <span class="ss">class: </span><span class="no">Integer</span><span class="p">)</span> <span class="c1"># => true</span>
</code></pre>
<p>Currently, if we want such an API we have to use <code>**rest</code>:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">def</span> <span class="nf">check</span><span class="p">(</span><span class="n">arg</span><span class="p">,</span> <span class="o">**</span><span class="n">rest</span><span class="p">)</span>
<span class="n">class_</span> <span class="o">=</span> <span class="n">rest</span><span class="p">.</span><span class="nf">fetch</span><span class="p">(</span><span class="ss">:class</span><span class="p">)</span> <span class="p">{</span> <span class="k">raise</span> <span class="no">ArgumentError</span><span class="p">(</span><span class="s1">'missing keyword: :class'</span><span class="p">)}</span>
<span class="k">if</span> <span class="n">rest</span><span class="p">.</span><span class="nf">size</span> <span class="o">></span> <span class="mi">1</span>
<span class="n">unknown</span> <span class="o">=</span> <span class="n">rest</span><span class="p">.</span><span class="nf">keys</span> <span class="o">-</span> <span class="p">[</span><span class="ss">:class</span><span class="p">]</span>
<span class="k">raise</span> <span class="no">ArgumentError</span><span class="p">(</span><span class="s2">"unknown keyword(s): :</span><span class="si">#{</span><span class="n">unknown</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="s1">', :'</span><span class="p">)</span><span class="si">}</span><span class="s2">)
end
arg.is_a?(class_)
end
</span></code></pre>
<p>This is very verbose, much less convenient, much less readable, prevents <code>steep</code> from generating the proper signature, etc.</p>
<p>We should do the same for pattern match.</p> Ruby master - Feature #17601 (Closed): lib/benchmark: adding `Benchmark::Tms#to_h`https://bugs.ruby-lang.org/issues/176012021-01-31T17:11:24Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>It seems useful to add <code>to_h</code> method to benchmark output.</p>
<p>I'll take care of that unless there's objection.</p>
<p>See <a href="https://github.com/ruby/benchmark/pull/4" class="external">https://github.com/ruby/benchmark/pull/4</a></p> Ruby master - Bug #17600 (Closed): lib/benchmark should use `$stdout` instead of `STDOUT`https://bugs.ruby-lang.org/issues/176002021-01-31T17:09:39Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Unless there is objection, I'll change all uses of <code>STDOUT</code> to <code>$stdout</code> in <code>lib/benchmark</code>, to allow redirection to other io.</p>
<p>See <a href="https://github.com/ruby/benchmark/issues/5" class="external">https://github.com/ruby/benchmark/issues/5</a></p> Ruby master - Feature #17592 (Closed): Ractor should allowing reading shareable class instance va...https://bugs.ruby-lang.org/issues/175922021-01-29T15:29:19Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>It would be very helpful if Ractor was allowing reading class instance variables from non-main Ractor.</p>
<p>Currently is raises an IsolationError:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">module</span> <span class="nn">Foo</span>
<span class="n">singleton_class</span><span class="p">.</span><span class="nf">attr_accessor</span> <span class="ss">:config</span>
<span class="no">Foo</span><span class="p">.</span><span class="nf">config</span> <span class="o">=</span> <span class="p">{</span><span class="ss">example: </span><span class="mi">42</span><span class="p">}.</span><span class="nf">freeze</span>
<span class="k">end</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="nb">p</span> <span class="no">Foo</span><span class="p">.</span><span class="nf">config</span> <span class="p">}</span> <span class="c1"># => IsolationError</span>
</code></pre>
<p>This limitation makes it challenging to have an efficient way to store general configs, i.e. global data that mutated a few times when resources get loaded but it immutable afterwards, and needs to be read all the time.</p>
<p>Currently the only way to do this is to use a constant and use <code>remove_const</code> + <code>const_set</code> (which can not be made atomic easily).</p>
<p>I think that allowing reading only may be the best solution to avoid any race condition, e.g. two different Ractors that call <code>@counter += 1</code>.</p>
<p>The only 3 scenarios I see here are:<br>
0) declare the constant hack the official way to store config-style data</p>
<ol>
<li>allow reading of instance variables for shareable objects (as long as the data is shareable)</li>
<li>allow read-write</li>
</ol>
<p>I prefer 1)</p> Ruby master - Bug #17577 (Closed): Segfault when sending some Exceptions to a Ractorhttps://bugs.ruby-lang.org/issues/175772021-01-24T19:03:45Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>The following segfaults:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">r</span> <span class="o">=</span> <span class="no">Ractor</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="n">receive</span> <span class="p">}</span>
<span class="k">begin</span>
<span class="n">foo</span> <span class="c1"># => raises a NoMethodError</span>
<span class="k">rescue</span> <span class="no">Exception</span> <span class="o">=></span> <span class="n">e</span>
<span class="n">r</span><span class="p">.</span><span class="nf">send</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
<span class="k">end</span>
<span class="nb">p</span> <span class="n">r</span><span class="p">.</span><span class="nf">take</span>
</code></pre>
<p>Note that some exceptions are ok:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">raise</span> <span class="c1"># => ok</span>
<span class="k">raise</span> <span class="no">NoMethodError</span><span class="p">,</span> <span class="s1">'...'</span> <span class="c1"># => ok</span>
<span class="s1">'s'</span><span class="p">.</span><span class="nf">to_i</span><span class="p">(</span><span class="o">-</span><span class="mi">2</span><span class="p">)</span> <span class="c1"># => ok</span>
<span class="n">foo</span> <span class="c1"># => segfault</span>
<span class="no">Xyz</span> <span class="c1"># => segfault</span>
</code></pre>
<p>Present on 3.0.0p0 as well as master (ruby 3.1.0dev (2021-01-24T14:26:11Z master 30f11e73c4) [x86_64-darwin18])</p>
<pre><code>-- Control frame information -----------------------------------------------
c:0004 p:0003 s:0019 e:000018 METHOD <internal:ractor>:583
c:0003 p:0025 s:0012 e:000011 RESCUE ractor.rb:5
c:0002 p:0025 s:0008 E:000680 EVAL ractor.rb:2 [FINISH]
c:0001 p:0000 s:0003 E:001b80 (none) [FINISH]
-- C level backtrace information -------------------------------------------
/Users/mal/.rbenv/versions/dev/bin/ruby(rb_vm_bugreport+0x732) [0x105de8672]
/Users/mal/.rbenv/versions/dev/bin/ruby(rb_bug_for_fatal_signal+0x1dc) [0x105bf671c]
/Users/mal/.rbenv/versions/dev/bin/ruby(sigsegv+0x5b) [0x105d3c87b]
/usr/lib/system/libsystem_platform.dylib(_sigtramp+0x1d) [0x7fff61425b5d]
[0x34]
/Users/mal/.rbenv/versions/dev/bin/ruby(rb_objspace_reachable_objects_from+0xa3) [0x105c19193]
/Users/mal/.rbenv/versions/dev/bin/ruby(obj_traverse_replace_i+0x38d) [0x105cedb0d]
/Users/mal/.rbenv/versions/dev/bin/ruby(obj_traverse_replace_i+0x46f) [0x105cedbef]
/Users/mal/.rbenv/versions/dev/bin/ruby(ractor_send+0x18b) [0x105ce8fdb]
/Users/mal/.rbenv/versions/dev/bin/ruby(vm_exec_core+0x77ea) [0x105dbfcaa]
/Users/mal/.rbenv/versions/dev/bin/ruby(rb_vm_exec+0xb36) [0x105dd0916]
/Users/mal/.rbenv/versions/dev/bin/ruby(rb_ec_exec_node+0x14d) [0x105c01aad]
/Users/mal/.rbenv/versions/dev/bin/ruby(ruby_run_node+0x57) [0x105c018f7]
/Users/mal/.rbenv/versions/dev/bin/ruby(main+0x71) [0x105b51141]
</code></pre> Ruby master - Misc #17569 (Open): `uri` lib maintainershiphttps://bugs.ruby-lang.org/issues/175692021-01-22T15:48:33Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>I’d like to merge <a href="https://github.com/ruby/uri/pull/15" class="external">https://github.com/ruby/uri/pull/15</a> but it is an API change. I would release v1.0.0. <a class="user active user-mention" href="https://bugs.ruby-lang.org/users/271">@akr (Akira Tanaka)</a> is the official maintainer of <code>uri</code>… Is he still interested in this role? Otherwise we could put “Ruby core team” in the listing…</p> Ruby master - Bug #17543 (Closed): Ractor isolation broken by `self` in shareable prochttps://bugs.ruby-lang.org/issues/175432021-01-15T07:39:58Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Discussing with <a class="user active user-mention" href="https://bugs.ruby-lang.org/users/8409">@MaxLap (Maxime Lapointe)</a> we realized that the <code>self</code> in a shareable proc is not properly isolated:</p>
<pre><code>class Foo
attr_accessor :x
def pr
Ractor.make_shareable(Proc.new { self })
end
end
f = Foo.new
f.x = [1, 2, 3]
Ractor.new(f.pr) { |pr| pr.call.x << :oops }
p f.x # => [1, 2, 3, :oops]
</code></pre>
<p>If the <code>self</code> refers to a shareable object then it's fine, but for non-shareable objects it has to be reset to <code>nil</code> or to a global shareable object that would have an instructive <code>inspect</code>.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">Ractor</span><span class="o">::</span><span class="no">DETACHED_SELF</span> <span class="o">=</span> <span class="no">Object</span><span class="p">.</span><span class="nf">new</span>
<span class="k">def</span> <span class="nf"><<</span> <span class="no">Ractor</span><span class="o">::</span><span class="no">DETACHED_SELF</span>
<span class="k">def</span> <span class="nf">inspect</span>
<span class="s1">'<#detached self>'</span>
<span class="k">end</span>
<span class="k">alias</span> <span class="nb">to_s</span> <span class="nb">inspect</span>
<span class="k">end</span>
<span class="no">Ractor</span><span class="o">::</span><span class="no">DETACHED_SELF</span><span class="p">.</span><span class="nf">freeze</span>
</code></pre> Ruby master - Bug #17531 (Closed): `did_you_mean` not Ractor friendlyhttps://bugs.ruby-lang.org/issues/175312021-01-12T19:30:56Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>The gem <code>did_you_mean</code> uses a class instance variable that does not work with Ractor.</p>
<pre><code>$ RUBYOPT='--disable-did_you_mean' ruby -W0 -e 'Ractor.new{ begin ; nil + 42; rescue Exception => e; e.to_s end}.take'
# => prints nothing (ok)
$ ruby -W0 -e 'Ractor.new{ begin ; nil + 42; rescue Exception => e; e.to_s end}.take'
# Prints
/Users/mal/.rbenv/versions/3.0.0/lib/ruby/3.0.0/did_you_mean.rb:102:in `formatter'
/Users/mal/.rbenv/versions/3.0.0/lib/ruby/3.0.0/did_you_mean/core_ext/name_error.rb:9:in `to_s'
-e:1:in `rescue in block in <main>'
-e:1:in `block in <main>'
# (not ok)
</code></pre> Ruby master - Feature #17513 (Open): Methods of shareable objects and UnboundMethods should be sh...https://bugs.ruby-lang.org/issues/175132021-01-06T06:04:18Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">Foo</span>
<span class="k">def</span> <span class="nf">foo</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">f</span> <span class="o">=</span> <span class="no">Foo</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">freeze</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">shareable?</span><span class="p">(</span><span class="n">f</span><span class="p">)</span> <span class="c1"># => true</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">make_shareable</span><span class="p">(</span><span class="n">f</span><span class="p">.</span><span class="nf">method</span><span class="p">(</span><span class="ss">:foo</span><span class="p">).</span><span class="nf">to_proc</span><span class="p">)</span> <span class="c1"># => Proc, ok</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">make_shareable</span><span class="p">(</span><span class="n">f</span><span class="p">.</span><span class="nf">method</span><span class="p">(</span><span class="ss">:foo</span><span class="p">))</span> <span class="c1"># => Ractor::Error, expected Method</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">make_shareable</span><span class="p">(</span><span class="no">Foo</span><span class="p">.</span><span class="nf">instance_method</span><span class="p">(</span><span class="ss">:foo</span><span class="p">))</span> <span class="c1"># => Ractor::Error, expected UnboundMethod</span>
</code></pre> Ruby master - Bug #17506 (Open): Ractor isolation broken by ThreadGrouphttps://bugs.ruby-lang.org/issues/175062021-01-03T20:05:43Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Ractors currently share the ThreadGroup.</p>
<p>This doesn't seem very useful as there is no possible communication between the Threads of different Ractors.</p>
<p>It is also an isolation error:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">ThreadGroup</span><span class="p">.</span><span class="nf">attr_accessor</span> <span class="ss">:foo</span>
<span class="n">var</span> <span class="o">=</span> <span class="no">Thread</span><span class="p">.</span><span class="nf">current</span><span class="p">.</span><span class="nf">group</span><span class="p">.</span><span class="nf">foo</span> <span class="o">=</span> <span class="p">[</span><span class="ss">:example</span><span class="p">]</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="no">Thread</span><span class="p">.</span><span class="nf">current</span><span class="p">.</span><span class="nf">group</span><span class="p">.</span><span class="nf">foo</span> <span class="o"><<</span> <span class="p">[</span><span class="ss">:oops</span><span class="p">]</span> <span class="p">}.</span><span class="nf">take</span>
<span class="n">var</span> <span class="c1"># => [:example, [:oops]]</span>
</code></pre>
<p>Should <code>Ractor.new</code> create a new <code>ThreadGroup</code>? Should <code>ThreadGroup</code> not have (non-shareable) instance variables? Or should <code>Ractor.new { Thread.current.group }.take</code> be <code>nil</code>? See also <a href="https://bugs.ruby-lang.org/issues/17505" class="external">https://bugs.ruby-lang.org/issues/17505</a> about <code>nil</code>.</p>
<p>Note that <code>Ractor</code> respects the <code>ThreadGroup</code>'s state:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">Thread</span><span class="p">.</span><span class="nf">current</span><span class="p">.</span><span class="nf">group</span><span class="p">.</span><span class="nf">enclose</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="no">Thread</span><span class="p">.</span><span class="nf">current</span><span class="p">.</span><span class="nf">group</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span><span class="no">Thread</span><span class="p">.</span><span class="nf">current</span><span class="p">)</span> <span class="p">}.</span><span class="nf">take</span> <span class="c1"># => can't move to the enclosed thread group</span>
<span class="no">Thread</span><span class="p">.</span><span class="nf">current</span><span class="p">.</span><span class="nf">group</span><span class="p">.</span><span class="nf">freeze</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">new</span> <span class="p">{}</span> <span class="c1"># => ThreadError (can't start a new thread (frozen ThreadGroup))</span>
</code></pre>
<p>I am not sure what is the best behavior as I don't have enough experience with how ThreadGroups are used, especially enclosed ThreadGroups.</p> Ruby master - Bug #17505 (Closed): Can `Thread#group` actually be `nil`?https://bugs.ruby-lang.org/issues/175052021-01-03T19:41:27Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Is there a circumstance where <code>Thread#group</code> could actually be <code>nil</code>?</p>
<p>The documentation says so, there seems to be source code for this, but I can find no test or RubySpec for this and I don't see anywhere in the <code>ThreadGroup</code> API that could allow this.</p> Ruby master - Misc #17502 (Open): C vs Rubyhttps://bugs.ruby-lang.org/issues/175022021-01-02T20:25:28Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Some features are coded in a mix of Ruby and C (e.g. ractor.rb).</p>
<p>External gems don't have access to this. The C-API to deal with keyword parameters is also very verbose the parsing and the engine does not know it.</p>
<p>Moreover, some optimization PRs are simply rewriting C-code into Ruby using pseudo C code.</p>
<p>I understand the intentions are great, but changes like <a href="https://github.com/ruby/ruby/pull/4018/files" class="external">https://github.com/ruby/ruby/pull/4018/files</a> seem a symptom that something needs to be improved with the C api.</p>
<pre><code class="diff syntaxhl" data-language="diff"><span class="gd">-static VALUE
- flo_zero_p(VALUE num)
- {
- return flo_iszero(num) ? Qtrue : Qfalse;
- }
</span># in different file:
<span class="gi">+ def zero?
+ Primitive.attr! 'inline'
+ Primitive.cexpr! 'flo_iszero(self) ? Qtrue : Qfalse'
+ end
</span></code></pre>
<p>It seems to me that this is a way to circumvent a deeper issue. Is this the right direction?</p>
<p>Is there a plan for an API that would:</p>
<ol>
<li>be accessible to C extensions</li>
<li>can't be re-written any faster in pseuso-C in Ruby</li>
<li>has an easy way to define keyword parameters?</li>
</ol>
<p>I realize that RBS may give perfect signatures, but ideally <code>parameters</code> would be more informative for C-functions too.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">Ractor</span><span class="p">.</span><span class="nf">method</span><span class="p">(</span><span class="ss">:yield</span><span class="p">).</span><span class="nf">parameters</span>
<span class="c1"># => [[:req, :obj], [:key, :move]] # good!</span>
<span class="no">Fiber</span><span class="p">.</span><span class="nf">method</span><span class="p">(</span><span class="ss">:initialize</span><span class="p">).</span><span class="nf">parameters</span>
<span class="c1"># => [[:rest]] # not good, should be [[:key, :blocking]]</span>
</code></pre> Ruby master - Bug #17497 (Closed): Ractor performance issuehttps://bugs.ruby-lang.org/issues/174972020-12-31T21:48:50Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>There's a strange performance issue with Ractor (at least on MacOS, didn't run on other OS).</p>
<p>I ran a benchmark doing 3 different types of work:</p>
<ul>
<li>"fib": method calls (naive fibonacci calculation)</li>
<li>"cpu": <code>(0...1000).inject(:+)</code>
</li>
<li>"sleep": call <code>sleep</code>
</li>
</ul>
<p>I get the kind of results I was excepting for the <code>fib</code> and for sleeping, but the results for the "cpu" workload show a problem.</p>
<p>It is so slow that my pure Ruby backport (using Threads) is 65x faster 😮 on my Mac Pro (despite having 6 cores). Expected results would be 6x slower, so in that case Ractor is 400x slower than it should 😿</p>
<p>On my MacBook (2 cores) the results are not as bad, the <code>cpu</code> workload is 3x faster with my pure-Ruby backport (only) instead of ~2x slower, so the factor is 6x too slow.</p>
<pre><code>$ gem install backports
Successfully installed backports-3.20.0
1 gem installed
$ ruby ractor_test.rb
<internal:ractor>:267: warning: Ractor is experimental, and the behavior may change in future versions of Ruby! Also there are many implementation issues.
fib: 110 ms | cpu: 22900 ms | sleep: 206 ms
$ B=t ruby ractor_test.rb
Using pure Ruby implementation
fib: 652 ms | cpu: 337 ms | sleep: 209 ms
</code></pre>
<p>Notice the <code>sleep</code> run takes similar time, which is good, and <code>fib</code> is ~6x faster on my 6-core CPU (and ~2x faster on my 2-core MacBook), again that's good as the pure ruby version uses Threads and thus runs with a single GVL.</p>
<p>The <code>cpu</code> version is the problem.</p>
<p>Script is here: <a href="https://gist.github.com/marcandre/bfed626e538a3d0fc7cad38dc026cf0e" class="external">https://gist.github.com/marcandre/bfed626e538a3d0fc7cad38dc026cf0e</a></p> Ruby master - Bug #17428 (Closed): Method#inspect bad output for class methodshttps://bugs.ruby-lang.org/issues/174282020-12-23T00:35:55Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<pre><code>$ $ ruby -e 'p String.method(:prepend)'
# 2.7.0:
#<Method: String.prepend(*)>
# 3.0.0:
#<Method: #<Class:Object>(Module)#prepend(*)>
</code></pre>
<p>@jeremyevans found it shows the method as pertaining to one level too high (which is good for objects as we don't want to show the singleton class there, but not for classes).</p>
<p>Probably due to <a class="issue tracker-1 status-5 priority-4 priority-default closed" title="Bug: What should be the correct output for Method#inspect with singleton methods? (Closed)" href="https://bugs.ruby-lang.org/issues/15608">#15608</a></p> Ruby master - Feature #17418 (Closed): Add `Ractor.main?` and `Ractor.main`https://bugs.ruby-lang.org/issues/174182020-12-21T16:25:20Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Since main Ractor is special, it seems useful to have an easy way to check if the current ractor is the main ractor.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">Ractor</span><span class="p">.</span><span class="nf">main?</span> <span class="c1"># => true</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="no">Ractor</span><span class="p">.</span><span class="nf">main?</span> <span class="p">}.</span><span class="nf">take</span> <span class="c1"># => false</span>
</code></pre>
<p>As far as I know, a gem could be loaded from a non-main Ractor so there is no reliable way for a gem to know the main Ractor (except than trying to do something that is not allowed)</p>
<p>We might as well add <code>Ractor.main</code> to return the main Ractor (probably less useful though).</p> Ruby master - Feature #17414 (Open): Ractor should allow access to shareable attributes for Modul...https://bugs.ruby-lang.org/issues/174142020-12-21T01:55:02Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Current situation is <em>very</em> limiting.</p>
<p>Use-case: global config.</p>
<p>Example: <a href="https://github.com/ruby/psych/blob/master/lib/psych.rb#L637-L640" class="external">yaml has a global config</a> and it's not clear to me how to make that Ractor-aware (nicely).</p>
<p>It is possible to have the same effect but in ugly ways:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># Using instance variables of Module not allowed:</span>
<span class="k">module</span> <span class="nn">Config</span>
<span class="k">class</span> <span class="o"><<</span> <span class="nb">self</span>
<span class="nb">attr_accessor</span> <span class="ss">:conf</span>
<span class="k">end</span>
<span class="nb">self</span><span class="p">.</span><span class="nf">conf</span> <span class="o">=</span> <span class="mi">42</span>
<span class="k">end</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="no">Config</span><span class="p">.</span><span class="nf">conf</span> <span class="o">=</span> <span class="mi">66</span> <span class="p">}.</span><span class="nf">take</span> <span class="c1"># => can not access instance variables from non-main Ractors</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="nb">puts</span> <span class="no">Config</span><span class="p">.</span><span class="nf">conf</span> <span class="p">}.</span><span class="nf">take</span> <span class="c1"># => can not access instance variables from non-main Ractors</span>
<span class="c1"># Same functionality using constants allowed:</span>
<span class="k">module</span> <span class="nn">Config</span>
<span class="k">class</span> <span class="o"><<</span> <span class="nb">self</span>
<span class="k">def</span> <span class="nf">conf</span>
<span class="no">CONF</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">conf</span><span class="o">=</span><span class="p">(</span><span class="n">new_conf</span><span class="p">)</span>
<span class="n">remove_const</span><span class="p">(</span><span class="ss">:CONF</span><span class="p">)</span>
<span class="nb">const_set</span><span class="p">(</span><span class="ss">:CONF</span><span class="p">,</span> <span class="n">new_conf</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="no">CONF</span> <span class="o">=</span> <span class="mi">42</span>
<span class="k">end</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="no">Config</span><span class="p">.</span><span class="nf">conf</span> <span class="o">=</span> <span class="mi">66</span> <span class="p">}.</span><span class="nf">take</span> <span class="c1"># => ok</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="nb">puts</span> <span class="no">Config</span><span class="p">.</span><span class="nf">conf</span> <span class="p">}.</span><span class="nf">take</span> <span class="c1"># => 66</span>
<span class="c1"># Same functionality using methods allowed:</span>
<span class="k">module</span> <span class="nn">Config</span>
<span class="k">class</span> <span class="o"><<</span> <span class="nb">self</span>
<span class="k">def</span> <span class="nf">conf</span>
<span class="mi">42</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">conf</span><span class="o">=</span><span class="p">(</span><span class="n">new_conf</span><span class="p">)</span>
<span class="n">singleton_class</span><span class="p">.</span><span class="nf">undef_method</span><span class="p">(</span><span class="ss">:conf</span><span class="p">)</span>
<span class="n">define_singleton_method</span><span class="p">(</span><span class="ss">:conf</span><span class="p">,</span> <span class="o">&</span><span class="no">Ractor</span><span class="p">.</span><span class="nf">make_shareable</span><span class="p">(</span><span class="no">Proc</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="n">new_conf</span> <span class="p">}))</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="no">Config</span><span class="p">.</span><span class="nf">conf</span> <span class="o">=</span> <span class="mi">66</span> <span class="p">}.</span><span class="nf">take</span> <span class="c1"># => ok</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="nb">puts</span> <span class="no">Config</span><span class="p">.</span><span class="nf">conf</span> <span class="p">}.</span><span class="nf">take</span> <span class="c1"># => 66</span>
</code></pre>
<p>The priority would be to allow reading these instance variables if they are shareable. Ideally writing would also be allowed, but limiting that to main ractor is less probablematic than with reading.</p> Ruby master - Feature #17406 (Open): Add `NoMatchingPatternError#depth`https://bugs.ruby-lang.org/issues/174062020-12-18T21:45:35Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Could we have <code>NoMatchingPatternError#depth</code>, returning the number of <code>case...in...end</code> an exception has traversed?</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">def</span> <span class="nf">show_depth</span>
<span class="k">yield</span>
<span class="k">rescue</span> <span class="no">NoMatchingPatternError</span> <span class="o">=></span> <span class="n">e</span>
<span class="nb">puts</span> <span class="s2">"Depth: </span><span class="si">#{</span><span class="n">e</span><span class="p">.</span><span class="nf">depth</span><span class="si">}</span><span class="s2">"</span>
<span class="k">raise</span>
<span class="k">end</span>
<span class="n">show_depth</span> <span class="k">do</span>
<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="mi">3</span><span class="p">]</span>
<span class="k">in</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="k">then</span>
<span class="n">show_depth</span> <span class="k">do</span>
<span class="n">x</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="c1"># => raises NoMatchingPatternError</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="c1"># Prints "Depth: 0" then "Depth: 1"</span>
</code></pre>
<p>This could help bring pattern match closer to a language construct people can play with.</p>
<p>Example usecase: implement <code>Ractor#receive_if</code> as in <a href="https://bugs.ruby-lang.org/issues/17378#note-6" class="external">https://bugs.ruby-lang.org/issues/17378#note-6</a></p> Ruby master - Feature #17404 (Open): Ractor `move:` API to allow shareability checkhttps://bugs.ruby-lang.org/issues/174042020-12-17T18:29:50Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>I'd like to <code>ractor.send(message)</code> and express that <code>message</code> should be shareable. Currently I'm given two choices: <code>move: true</code> and <code>move: false</code> / nothing, neither of which have an effect if my <code>message</code> is shareable, and neither of which will tell me in case there's a bug in my program and <code>message</code> is not shareable.</p>
<p>Could we consider a slightly different API (for 3.0 or 3.1)?</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">ractor</span><span class="p">.</span><span class="nf">send</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="ss">pass: :copy</span><span class="p">)</span> <span class="c1"># => like current `move: false`</span>
<span class="n">ractor</span><span class="p">.</span><span class="nf">send</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="ss">pass: :move</span><span class="p">)</span> <span class="c1"># => like current `move: true`</span>
<span class="n">ractor</span><span class="p">.</span><span class="nf">send</span><span class="p">(</span><span class="n">message</span><span class="p">,</span> <span class="ss">pass: :share</span><span class="p">)</span> <span class="c1"># => raise in case message is not shareable</span>
<span class="n">ractor</span><span class="p">.</span><span class="nf">send</span><span class="p">(</span><span class="n">message</span><span class="p">)</span> <span class="c1"># => same as `pass: :copy`</span>
</code></pre> Ruby master - Feature #17401 (Closed): The class `Ractor::MovedObject` should not be frozenhttps://bugs.ruby-lang.org/issues/174012020-12-17T00:59:09Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>If <code>Ractor::MovedObject</code> can be not frozen that would be more helpful than currently. For example, to implement a helpful <code>inspect</code> in pure Ruby 3.0 like in <a class="issue tracker-2 status-1 priority-4 priority-default" title="Feature: `Ractor::Moved#inspect` (Open)" href="https://bugs.ruby-lang.org/issues/17393">#17393</a></p>
<p>It is currently possible to go around the fact that it is frozen with refinements:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">using</span> <span class="no">Module</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span>
<span class="n">refine</span> <span class="no">Ractor</span><span class="o">::</span><span class="no">MovedObject</span> <span class="k">do</span>
<span class="k">def</span> <span class="nf">inspect</span>
<span class="s2">"I was moved! Don't use me!"</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="p">}</span>
<span class="n">o</span> <span class="o">=</span> <span class="no">Object</span><span class="p">.</span><span class="nf">new</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">new</span><span class="p">{</span> <span class="n">receive</span> <span class="p">}.</span><span class="nf">send</span><span class="p">(</span><span class="n">o</span><span class="p">,</span> <span class="n">move</span><span class="ss">:true</span><span class="p">)</span>
<span class="nb">puts</span> <span class="n">o</span><span class="p">.</span><span class="nf">inspect</span> <span class="c1"># => "I was moved! Don't use me!"</span>
</code></pre>
<p>Another (ugly) way to bypass the fact that it is frozen without refinements is to modify its ancestor:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">module</span> <span class="nn">Ext</span>
<span class="k">def</span> <span class="nf">inspect</span>
<span class="k">return</span> <span class="k">super</span> <span class="k">unless</span> <span class="no">Ractor</span><span class="o">::</span><span class="no">MovedObject</span> <span class="o">===</span> <span class="nb">self</span>
<span class="s2">"I was moved! Don't use me!"</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="no">BasicObject</span><span class="p">.</span><span class="nf">prepend</span> <span class="no">Ext</span>
<span class="n">o</span> <span class="o">=</span> <span class="no">Object</span><span class="p">.</span><span class="nf">new</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">new</span><span class="p">{</span> <span class="n">receive</span> <span class="p">}.</span><span class="nf">send</span><span class="p">(</span><span class="n">o</span><span class="p">,</span> <span class="n">move</span><span class="ss">:true</span><span class="p">)</span>
<span class="nb">p</span> <span class="n">o</span> <span class="c1"># => "I was moved! Don't use me!"</span>
</code></pre>
<p>My point remain that this "security" can be bypassed unless it is stricly necessary it, it would be nice to unfreeze the class. It could be documented that this is experimental / not recommended to modify the class in production, for example.</p> Ruby master - Feature #17397 (Closed): `shareable_constant_value: literal` should check at runtim...https://bugs.ruby-lang.org/issues/173972020-12-16T18:25:35Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>I think <code>shareable_constant_value: literal</code> is too strict because it has too crude checks at parse time.</p>
<p>I wish the following code would parse and run:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># shareable_constant_value: literal</span>
<span class="k">class</span> <span class="nc">Foo</span> <span class="o"><</span> <span class="no">RuntimeError</span>
<span class="k">end</span>
<span class="c1"># Similar code, but does not parse:</span>
<span class="no">Bar</span> <span class="o">=</span> <span class="no">Class</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="no">RuntimeError</span><span class="p">)</span> <span class="c1"># => unshareable expression</span>
<span class="no">Baz</span> <span class="o">=</span> <span class="no">Ractor</span><span class="p">.</span><span class="nf">make_shareable</span><span class="p">(</span><span class="n">anything_here</span><span class="p">)</span> <span class="c1"># => unshareable expression</span>
<span class="no">Qux</span> <span class="o">=</span> <span class="no">Set</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="nf">freeze</span> <span class="c1"># => unshareable expression</span>
</code></pre>
<p>Could we instead raise some sort of RuntimeError when an assignment is made that is not shareable?</p> Ruby master - Feature #17393 (Open): `Ractor::Moved#inspect`https://bugs.ruby-lang.org/issues/173932020-12-14T20:48:49Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>It could be helpful to define <code>Ractor::Moved#inspect</code> and output the source location of when the data was moved. If preferred, it could raise an error with this information:</p>
<pre><code>x = []
Ractor.new{ receive }.send(x, move: true)
p x # => "Data was moved in `example.rb:4`"
# or
p x # => "Data was moved in `example.rb:4`" (Ractor::MovedError)
</code></pre>
<p>Also <a class="user active user-mention" href="https://bugs.ruby-lang.org/users/710">@zverok (Victor Shepelev)</a> and myself were wondering if there was a technical reason to freeze <code>Ractor::Moved</code>? If not, is it only to "force" people to use refinements (which are allowed on frozen classes)? It's already known that it is in general a bad idea to modify builtin classes, so it's not clear to me that freezing that class is best.</p> Ruby master - Feature #17380 (Closed): Useful `include/prepend` in `refine`https://bugs.ruby-lang.org/issues/173802020-12-09T04:31:11Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Currently, <code>prepend/include</code> within a <code>refine</code> block leads to a method not being to see itself, or others defined in the same module:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">module</span> <span class="nn">Code</span>
<span class="k">def</span> <span class="nf">recurse</span><span class="p">(</span><span class="n">value</span> <span class="o">=</span> <span class="kp">nil</span><span class="p">)</span>
<span class="k">return</span> <span class="n">value</span> <span class="k">if</span> <span class="n">value</span>
<span class="n">recurse</span><span class="p">(</span><span class="mi">42</span><span class="p">)</span> <span class="c1"># => NoMethodError!!!</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">module</span> <span class="nn">Extension</span>
<span class="n">refine</span> <span class="no">Object</span> <span class="k">do</span>
<span class="kp">include</span> <span class="no">Code</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">using</span> <span class="no">Extension</span>
<span class="ss">:x</span><span class="p">.</span><span class="nf">recurse</span><span class="p">(</span><span class="ss">:y</span><span class="p">)</span> <span class="c1"># => :y (ok)</span>
<span class="ss">:x</span><span class="p">.</span><span class="nf">recurse</span> <span class="c1"># => NoMethodError, was hoping for 42</span>
</code></pre>
<p>I find this unintuitive and not useful.</p>
<p>The conclusion of the current situation from <a class="user active user-mention" href="https://bugs.ruby-lang.org/users/12">@shugo (Shugo Maeda)</a> and others is <a href="https://bugs.ruby-lang.org/issues/17374#note-9" class="external">"I don't recommend module inclusion to define refined methods"</a></p>
<p>Could we change this situation so it can be recommended to use it?</p>
<p>What I believe would be more useful and is what I expected was that <code>include/prepend</code> within a <code>Module</code> would bring in the current methods in the Module, with the current refinements activated.</p>
<p>One use-case in particular is to publish libraries where one can give the option to the user to either:</p>
<ul>
<li>call <code>using GreatExtension</code> in each and every file that need it</li>
<li>or <code>MyClass.prepend GreatExtension</code> once.</li>
</ul>
<p>While <a href="https://bugs.ruby-lang.org/issues/17374#note-8" class="external">Jeremy Evans found a way to do it</a>, it remains challenging and unnatural.</p> Ruby master - Bug #17379 (Closed): Refinement with modules redefinition bughttps://bugs.ruby-lang.org/issues/173792020-12-09T04:21:11Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Depending on the circumstance, a refinement can be modified even after being used:</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="ss">:base</span><span class="p">]</span>
<span class="k">end</span>
<span class="k">module</span> <span class="nn">M</span>
<span class="k">def</span> <span class="nf">foo</span>
<span class="k">super</span> <span class="o"><<</span> <span class="ss">:M</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">module</span> <span class="nn">Ext</span>
<span class="n">refine</span> <span class="no">Object</span> <span class="k">do</span>
<span class="kp">include</span> <span class="no">M</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">using</span> <span class="no">Ext</span>
<span class="nb">p</span> <span class="s1">'asd'</span><span class="p">.</span><span class="nf">foo</span> <span class="k">unless</span> <span class="no">ENV</span><span class="p">[</span><span class="s1">'SKIP'</span><span class="p">]</span> <span class="c1"># => [:base, :M] (ok)</span>
<span class="k">module</span> <span class="nn">M</span>
<span class="k">def</span> <span class="nf">foo</span>
<span class="k">super</span> <span class="o"><<</span> <span class="ss">:new_ref</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="nb">p</span> <span class="s1">'asd'</span><span class="p">.</span><span class="nf">foo</span> <span class="c1"># => depends (not ok)</span>
</code></pre>
<p>Running this gives:</p>
<pre><code>$ ruby refinement.rb
[:base, :M]
[:base, :M] # => ok
$ SKIP=t ruby refinement.rb
[:base, :new_ref] # => should be [:base, :M]
</code></pre> Ruby master - Bug #17374 (Rejected): Refined methods aren't visible from a refinement's modulehttps://bugs.ruby-lang.org/issues/173742020-12-07T05:25:04Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.caRuby master - Bug #17368 (Closed): `Ractor.select()` loops foreverhttps://bugs.ruby-lang.org/issues/173682020-12-05T07:41:44Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>I propose it raises an error in <a href="https://github.com/ruby/ruby/pull/3848" class="external">https://github.com/ruby/ruby/pull/3848</a></p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">Ractor</span><span class="p">.</span><span class="nf">select</span> <span class="c1"># => loops</span>
<span class="c1"># after</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">select</span> <span class="c1"># => ArgumentError, "specify at least one ractor or `yield_value`"</span>
</code></pre> Ruby master - Misc #17367 (Closed): CI: Env variable for Ractor compatibility tests?https://bugs.ruby-lang.org/issues/173672020-12-05T07:35:56Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>It will become important to test that some features are Ractor compatible.</p>
<p>For some builtin libs for example: that a frozen <code>Matrix</code>/<code>OpenStruct</code> is <code>Ractor.shareable?</code>, or that <code>Integer#prime?</code> works at all.</p>
<p>Could we have an official way to add <a href="https://github.com/ruby/ruby/commit/bc23216e5a4204b8e626704c7277e9edc1708189" class="external">these kinds of tests</a> to the codebase please?</p> Ruby master - Bug #17366 (Closed): Ractor odd issue with timeout + receive + sleep + takehttps://bugs.ruby-lang.org/issues/173662020-12-05T06:58:11Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>I believe the following behavior is incorrect:</p>
<pre><code>ruby -r timeout -e 'r = Ractor.new { Timeout.timeout(0.1) { sleep(1) } rescue :timeout }; p r.take'
# => :timeout (ok)
ruby -r timeout -e 'r = Ractor.new { Timeout.timeout(0.1) { sleep(1) } rescue :timeout }; sleep(0.2); p r.take'
# => :timeout (ok)
ruby -r timeout -e 'r = Ractor.new { Timeout.timeout(0.1) { Ractor.receive } rescue :timeout }; p r.take'
# => :timeout (ok)
ruby -r timeout -e 'r = Ractor.new { Timeout.timeout(0.1) { Ractor.receive } rescue :timeout }; sleep(0.2); p r.take'
<internal:ractor>:130:in `take': The outgoing-port is already closed (Ractor::ClosedError) # => not ok
</code></pre> Ruby master - Feature #17365 (Closed): Can not respond (well) to a Ractor https://bugs.ruby-lang.org/issues/173652020-12-04T05:05:31Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Summary: currently there is no good way to return a response to a Ractor message.</p>
<p>Sorry, this is long.<br>
Points 1 to 3 look at possible current solutions and why they aren't great.<br>
Point 4 discusses how Elixir/Erlang's builtin filtering allows responses.<br>
Last point proposes one of the many APIs that would allow responses.</p>
<p>Details:</p>
<p>If I want to program a "server" using Ractor, there has to be some way to receive the data from it.<br>
To simplify, say I want a global <code>Config</code> that can be used to set/retrieve some global config parameters.<br>
To set a parameter, I can use <code>server.send [:set, :key, 'value']</code>.<br>
But what about retrieving? There is no good way to achieve that with the current API.</p>
<ol>
<li>"pull" API</li>
</ol>
<p>It is not safe, as two clients could send a <code>:set</code> before the server answers, and the clients could resolve their <code>server.take</code> in the reverse order.</p>
<p>Another issue is that <code>Ractor.yield</code> is blocking, so the unexpected death of the client could mean the server hangs, and subsequent requests/responses are desynchronized and thus wrong.</p>
<p>My impression is that the "pull" API is best only used for monitoring of Ractors, rescuing exceptions, etc., or otherwise reserved for Ractors that are not shared, is this correct?</p>
<ol start="2">
<li>"push" API</li>
</ol>
<p>It seems much more appropriate to design a server such that one sends the client ractor with the push API. E.g. the client calls <code>server.send [:retrieve, :key, Ractor.current]</code>; the server can use the last element <code>cient_ractor</code> to respond with <code>client_ractor.send 'value'</code> that is non-blocking.</p>
<p>The client can then call <code>Ractor.receive</code>, immediately or later, to get the answer.</p>
<p>This is perfect, <em>except</em> that the client can not use <code>Ractor.receive</code> for any other purpose. It can not act itself a server, or if it calls multiple servers then it must do so synchroneously. Otherwise it might <code>receive</code> a request for something other than the response it was waiting for.</p>
<ol start="3">
<li>create Ractor + "push" + "pull"</li>
</ol>
<p>The only way I can think of currently is to create a temporary private Ractor (both to be able to use the "pull" and the "push" API):</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># on the client:</span>
<span class="n">response</span> <span class="o">=</span> <span class="no">Ractor</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">server</span><span class="p">,</span> <span class="o">*</span><span class="n">etc</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">server</span><span class="p">,</span> <span class="o">*</span><span class="n">etc</span><span class="o">|</span>
<span class="n">server</span><span class="p">.</span><span class="nf">send</span> <span class="p">[</span><span class="ss">:retrieve</span><span class="p">,</span> <span class="ss">:key</span><span class="p">,</span> <span class="no">Ractor</span><span class="p">.</span><span class="nf">current</span><span class="p">].</span><span class="nf">freeze</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">yield</span><span class="p">(</span><span class="no">Ractor</span><span class="p">.</span><span class="nf">receive</span><span class="p">,</span> <span class="ss">move: </span><span class="kp">true</span><span class="p">)</span>
<span class="p">}.</span><span class="nf">take</span>
<span class="c1"># on the server</span>
<span class="k">case</span> <span class="no">Ractor</span><span class="p">.</span><span class="nf">receive</span>
<span class="k">in</span> <span class="p">[</span><span class="ss">:retrieve</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">client_ractor</span><span class="p">]</span>
<span class="n">client_ractor</span><span class="p">.</span><span class="nf">send</span><span class="p">(</span><span class="s1">'response'</span><span class="p">)</span>
<span class="c1"># ...</span>
<span class="k">end</span>
</code></pre>
<p>I fear this would be quite inefficient (one Ractor per request, extra <code>move</code> of data) and seems very verbose.</p>
<ol start="4">
<li>Filtered <code>receive</code>
</li>
</ol>
<p>If I look at Elixir/Erlang, this is not an issue because the equivalent of <code>Ractor.receive</code> has builtin pattern matching.</p>
<p>The key is that unmatched messages are <a href="https://www.erlang-solutions.com/blog/receiving-messages-in-elixir-or-a-few-things-you-need-to-know-in-order-to-avoid-performance-issues.html#receiving-messages-with-%E2%80%9Ca-priority%E2%80%9D" class="external">queued for later retrieval</a>. This way there can be different <code>Ractor.receive</code> used in different ways in the same Ractor and they will not interact (assuming they use different patterns).</p>
<p>For a general server ("gen_server"), a unique tag is created for each request, that is <a href="https://stackoverflow.com/questions/56741322/gen-serverreply-2-format-of-message-sent-to-client" class="external">sent with the request and with the response</a></p>
<p>The same pattern is possible to implement with Ruby but this can only work if as long as all the <code>Ractor.receive</code> use this implementation in a given Ractor, it has to be thread-safe, etc.</p>
<p>Issue is that it may not be possible to have the same protocol and access to the same <code>receive</code> method, in particular if some of the functionality is provided in a gem.</p>
<ol start="5">
<li>In conclusion...</li>
</ol>
<p>The API of <code>Ractor</code> is currently lacking a good way to handle responses.</p>
<p>It needs to allow filtering/subdivision of the inbox in some way.</p>
<p>One API could be to add a <code>tag: nil</code> parameter to <code>Ractor#send</code> and <code>Ractor.receive</code> that would use that value to match.</p>
<p>A server could decide to use the default <code>nil</code> tag for it's main API, and ask its clients to specify a tag for a response:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">my_tag</span> <span class="o">=</span> <span class="ss">:some_return_tag</span>
<span class="n">server</span><span class="p">.</span><span class="nf">send</span><span class="p">(</span><span class="ss">:retrieve</span><span class="p">,</span> <span class="ss">:key</span><span class="p">,</span> <span class="no">Ractor</span><span class="p">.</span><span class="nf">current</span><span class="p">,</span> <span class="n">my_tag</span><span class="p">)</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">receive</span> <span class="ss">tag: </span><span class="n">my_tag</span>
<span class="c1"># on the server</span>
<span class="k">case</span> <span class="no">Ractor</span><span class="p">.</span><span class="nf">receive</span>
<span class="k">in</span> <span class="p">[</span><span class="ss">:retrieve</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">client_ractor</span><span class="p">,</span> <span class="n">client_tag</span><span class="p">]</span>
<span class="n">client_ractor</span><span class="p">.</span><span class="nf">send</span><span class="p">(</span><span class="s1">'response'</span><span class="p">,</span> <span class="ss">tag: </span><span class="n">client_tag</span><span class="p">)</span>
<span class="c1"># ...</span>
<span class="k">end</span>
</code></pre>
<p>Tags would have to be Ractor-shareable objects and they could be compared by identity.</p>
<p>Note that messages sent with a non-nil tag (e.g. <code>send 'value' tag: 42</code>) would not be matched by <code>Ractor.receive</code>.<br>
Maybe we should allow for a special <code>tag: :*</code> to match any tag?</p>
<p>There are other solutions possible; a request ID could be returned by <code>Ractor#send</code>, or there could be an API to create object for returns (like a "Self-addressed stamped envelope"), etc.</p>
<p>The basic filtering API I'm proposing has the advantage of being reasonable easy to implement efficiently and still allowing other patterns (for example handling messages by priority, assuming there can be a 0 timeout, see <a class="issue tracker-2 status-2 priority-4 priority-default" title="Feature: Timeouts (Assigned)" href="https://bugs.ruby-lang.org/issues/17363">#17363</a>), but I'll be happy as long as we can offer efficient and reliable builtin ways to respond to Ractor messages.</p> Ruby master - Feature #17363 (Assigned): Timeoutshttps://bugs.ruby-lang.org/issues/173632020-12-03T14:58:16Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Builtin methods like <code>Queue.pop</code> and <code>Ractor.receive</code> have no timeout parameter.</p>
<p>We should either:</p>
<ul>
<li>provide such a parameter</li>
<li>and/or provide a <code>Timeout::wake</code> that raises an timeout error only if the block is currently sleeping.</li>
</ul>
<p>Details:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">q</span> <span class="o">=</span> <span class="no">Queue</span><span class="p">.</span><span class="nf">new</span>
<span class="c1"># ...</span>
<span class="n">elem</span> <span class="o">=</span> <span class="no">Timeout</span><span class="o">::</span><span class="n">timeout</span><span class="p">(</span><span class="mi">42</span><span class="p">)</span> <span class="p">{</span> <span class="n">q</span><span class="p">.</span><span class="nf">pop</span> <span class="p">}</span> <span class="c1"># => It is possible that an element is retreived from the queue but never stored in `elem`</span>
<span class="n">elem</span> <span class="o">=</span> <span class="no">Timeout</span><span class="o">::</span><span class="n">wake</span><span class="p">(</span><span class="mi">42</span><span class="p">)</span> <span class="p">{</span> <span class="n">q</span><span class="p">.</span><span class="nf">pop</span> <span class="p">}</span> <span class="c1"># => Guaranteed that either element is retrieved from the queue or an exception is raised, never both</span>
<span class="no">Timeout</span><span class="o">::</span><span class="n">wake</span><span class="p">(</span><span class="mi">42</span><span class="p">)</span> <span class="p">{</span> <span class="kp">loop</span> <span class="p">{}</span> <span class="p">}</span> <span class="c1"># => infinite loop</span>
<span class="c1"># and/or</span>
<span class="n">elem</span> <span class="o">=</span> <span class="n">q</span><span class="p">.</span><span class="nf">pop</span><span class="p">(</span><span class="ss">timeout: </span><span class="mi">42</span><span class="p">)</span>
</code></pre>
<p>Currently, the only reliable way to have a Queue that accepts a timeout is to re-implement it from scratch. This post describe how involved that can be: <a href="https://spin.atomicobject.com/2017/06/28/queue-pop-with-timeout-fixed/" class="external">https://spin.atomicobject.com/2017/06/28/queue-pop-with-timeout-fixed/</a></p> Ruby master - Bug #17359 (Open): Ractor copy mode is not Ractor-safehttps://bugs.ruby-lang.org/issues/173592020-12-01T08:29:37Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>It should not be possible to mutate an object across Ractors, but the copy mode allows it currently:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">Foo</span>
<span class="nb">attr_accessor</span> <span class="ss">:x</span>
<span class="k">def</span> <span class="nf">initialize_copy</span><span class="p">(</span><span class="o">*</span><span class="p">)</span>
<span class="vg">$last</span> <span class="o">=</span> <span class="nb">self</span>
<span class="k">super</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">o</span> <span class="o">=</span> <span class="no">Foo</span><span class="p">.</span><span class="nf">new</span>
<span class="n">o</span><span class="p">.</span><span class="nf">x</span> <span class="o">=</span> <span class="mi">42</span>
<span class="n">r</span> <span class="o">=</span> <span class="no">Ractor</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">o</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">copy</span><span class="o">|</span>
<span class="nb">puts</span> <span class="n">copy</span><span class="p">.</span><span class="nf">x</span> <span class="c1"># => 42</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">yield</span> <span class="ss">:sync</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">yield</span> <span class="ss">:sync</span>
<span class="nb">puts</span> <span class="n">copy</span><span class="p">.</span><span class="nf">x</span> <span class="c1"># => 666</span>
<span class="k">end</span>
<span class="n">r</span><span class="p">.</span><span class="nf">take</span> <span class="c1"># => :sync</span>
<span class="vg">$last</span><span class="p">.</span><span class="nf">x</span> <span class="o">=</span> <span class="mi">666</span>
<span class="n">r</span><span class="p">.</span><span class="nf">take</span> <span class="c1"># => :sync</span>
<span class="n">r</span><span class="p">.</span><span class="nf">take</span>
</code></pre>
<p>Maybe the <code>copy</code> object should be marked as moved?</p> Ruby master - Feature #17357 (Open): `Queue#pop` should have a block form for closed queueshttps://bugs.ruby-lang.org/issues/173572020-12-01T01:42:16Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>It is currently difficult to reliably distinguish a <code>nil</code> value in a queue from the <code>nil</code> that is returned when a Queue is closed:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">n</span> <span class="o">=</span> <span class="mi">100_000</span>
<span class="n">result</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">t2</span> <span class="o">=</span> <span class="no">Thread</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="n">n</span><span class="p">.</span><span class="nf">times</span> <span class="p">{</span> <span class="no">Thread</span><span class="p">.</span><span class="nf">pass</span> <span class="p">}}</span> <span class="c1"># to make things less predictable</span>
<span class="n">n</span><span class="p">.</span><span class="nf">times</span><span class="p">.</span><span class="nf">count</span> <span class="k">do</span>
<span class="n">q</span> <span class="o">=</span> <span class="no">Queue</span><span class="p">.</span><span class="nf">new</span>
<span class="n">t</span> <span class="o">=</span> <span class="no">Thread</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="n">q</span><span class="p">.</span><span class="nf">pop</span><span class="p">;</span> <span class="n">result</span> <span class="o"><<</span> <span class="n">q</span><span class="p">.</span><span class="nf">closed?</span> <span class="p">}</span>
<span class="n">q</span> <span class="o"><<</span> <span class="kp">nil</span>
<span class="n">q</span><span class="p">.</span><span class="nf">close</span>
<span class="n">t</span><span class="p">.</span><span class="nf">join</span>
<span class="k">end</span>
<span class="nb">puts</span> <span class="n">result</span><span class="p">.</span><span class="nf">count</span><span class="p">(</span><span class="kp">true</span><span class="p">)</span> <span class="c1"># => some number usually > 9990 and < 10000</span>
</code></pre>
<p>To be completely sure, one needs a Mutex or wrap/unwrap <code>nil</code> values.</p>
<p><code>Queue#pop</code> should offer a surefire way to handle closed queues. I propose that an optional block be called in this case:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">q</span> <span class="o">=</span> <span class="no">Queue</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">close</span>
<span class="n">q</span><span class="p">.</span><span class="nf">pop</span> <span class="c1"># => nil</span>
<span class="n">q</span><span class="p">.</span><span class="nf">pop</span> <span class="p">{</span> <span class="ss">:closed</span> <span class="p">}</span> <span class="c1"># => :closed</span>
</code></pre>
<p>Proposed PR: <a href="https://github.com/ruby/ruby/pull/3830" class="external">https://github.com/ruby/ruby/pull/3830</a></p> Ruby master - Bug #17344 (Closed): `Ractor#shareable?` confused by recursive structureshttps://bugs.ruby-lang.org/issues/173442020-11-26T09:46:49Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">y</span> <span class="o">=</span> <span class="p">[];</span> <span class="n">x</span> <span class="o">=</span> <span class="p">[</span><span class="n">y</span><span class="p">,</span> <span class="p">{}].</span><span class="nf">freeze</span><span class="p">;</span> <span class="n">y</span> <span class="o"><<</span> <span class="n">x</span><span class="p">;</span> <span class="n">y</span><span class="p">.</span><span class="nf">freeze</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">shareable?</span><span class="p">(</span><span class="n">y</span><span class="p">)</span> <span class="c1"># => false, ok, the `{}` is not frozen</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">shareable?</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="c1"># => false, ok</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">shareable?</span><span class="p">(</span><span class="n">y</span><span class="p">)</span> <span class="c1"># => true, not ok!</span>
</code></pre>
<p>The error is that we can not mark anything as shareable until the whole tree has been searched successfully. Only when the full traversal is successful, then all visited objects can be marked as shareable. There might be a more clever way, but I couldn't think of one when working on my backport.</p> Ruby master - Bug #17343 (Closed): Ractor can't clone frozen structureshttps://bugs.ruby-lang.org/issues/173432020-11-26T09:44:49Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">Ractor</span><span class="p">.</span><span class="nf">new</span><span class="p">([[]].</span><span class="nf">freeze</span><span class="p">)</span> <span class="p">{}</span> <span class="c1"># => FrozenError</span>
</code></pre>
<p>See <a href="https://github.com/ruby/ruby/pull/3817" class="external">https://github.com/ruby/ruby/pull/3817</a></p> Ruby master - Misc #17329 (Closed): Doc for pattern match?https://bugs.ruby-lang.org/issues/173292020-11-17T01:59:34Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Is there an official doc for pattern matching?</p>
<p>I note that <code>case</code> in <a href="https://ruby-doc.org/core-2.7.2/doc/syntax/control_expressions_rdoc.html" class="external">https://ruby-doc.org/core-2.7.2/doc/syntax/control_expressions_rdoc.html</a> does not mention it, and <code>in</code> does not appear.</p> Ruby master - Bug #17310 (Closed): Closed ractors should diehttps://bugs.ruby-lang.org/issues/173102020-11-08T03:17:25Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>While backporting Ractors, I found this issue:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="mi">10</span><span class="p">.</span><span class="nf">times</span> <span class="p">{</span> <span class="no">Ractor</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="nb">sleep</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span>
<span class="nb">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="nb">puts</span> <span class="no">Ractor</span><span class="p">.</span><span class="nf">count</span> <span class="c1"># => 1, ok</span>
<span class="c1"># but:</span>
<span class="mi">10</span><span class="p">.</span><span class="nf">times</span> <span class="p">{</span> <span class="no">Ractor</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="nb">sleep</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span> <span class="p">}.</span><span class="nf">close</span> <span class="p">}</span>
<span class="nb">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">count</span> <span class="c1"># => 11, should be 1</span>
</code></pre> Ruby master - Feature #17286 (Open): `Ractor.new` should accept `move: true`https://bugs.ruby-lang.org/issues/172862020-10-26T05:10:00Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Another surprise when writing my backport is that <code>Ractor.new</code> does not accept <code>move:</code> keyword argument.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">Ractor</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">val</span><span class="p">,</span> <span class="ss">move: </span><span class="kp">true</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">data</span><span class="o">|</span> <span class="o">...</span> <span class="p">}</span>
<span class="c1"># equivalent to</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="n">data</span> <span class="o">=</span> <span class="no">Ractor</span><span class="p">.</span><span class="nf">receive</span><span class="p">;</span> <span class="o">...</span> <span class="p">}.</span><span class="nf">tap</span> <span class="p">{</span> <span class="o">|</span><span class="n">r</span><span class="o">|</span> <span class="n">r</span><span class="p">.</span><span class="nf">send</span><span class="p">(</span><span class="n">val</span><span class="p">,</span> <span class="ss">move: </span><span class="kp">true</span><span class="p">)</span> <span class="p">}</span>
</code></pre> Ruby master - Feature #17285 (Open): Less strict `Ractor.select`https://bugs.ruby-lang.org/issues/172852020-10-26T02:49:44Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Summary: could we have a way for <code>Ractor.select</code> to skip ractors with closed queues and raise only if no ractor with an open queue remains?</p>
<p>Detail:</p>
<p>I <a href="https://github.com/marcandre/backports/pull/153" class="external">backported <code>Ractor</code> for earlier Ruby versions</a>, as I'd like to use it in some gems that would work great in 3.0 and work ok in older Rubies without rewriting. That was a lot of fun :-)</p>
<p>One surprise for me was that <code>Ractor.select</code> enforces that no given ractor is terminated(*).</p>
<p>This means that one must remove terminated ractors from a pool of ractors before calling <code>select</code> again:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">pool</span> <span class="o">=</span> <span class="mi">20</span><span class="p">.</span><span class="nf">times</span><span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="no">Ractor</span><span class="p">.</span><span class="nf">new</span><span class="p">{</span> <span class="n">do_processing</span> <span class="p">}</span> <span class="p">}</span>
<span class="mi">20</span><span class="p">.</span><span class="nf">times</span> <span class="k">do</span>
<span class="n">ractor</span><span class="p">,</span> <span class="n">result</span> <span class="o">=</span> <span class="no">Ractor</span><span class="p">.</span><span class="nf">select</span><span class="p">(</span><span class="o">*</span><span class="n">pool</span><span class="p">)</span>
<span class="n">handle</span><span class="p">(</span><span class="n">result</span><span class="p">)</span>
<span class="n">pool</span><span class="p">.</span><span class="nf">delete</span><span class="p">(</span><span class="n">ractor</span><span class="p">)</span> <span class="c1"># necessary!</span>
<span class="k">end</span>
</code></pre>
<ol start="0">
<li>
<p>This can be tedious, but I know I'm very lazy</p>
</li>
<li>
<p>It is not convenient to share a pool between different ractors. Try writing code that starts 5 ractors that would consume the results from <code>pool</code> above.</p>
</li>
<li>
<p>It might require special synchronization if the ractors may yield a variable number of values:</p>
</li>
</ol>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">def</span> <span class="nf">do_processing</span>
<span class="nb">rand</span><span class="p">(</span><span class="mi">10</span><span class="p">).</span><span class="nf">times</span> <span class="k">do</span> <span class="p">{</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">yield</span> <span class="ss">:a_result</span>
<span class="p">}</span>
<span class="ss">:finish</span>
<span class="k">end</span>
<span class="n">pool</span> <span class="o">=</span> <span class="mi">20</span><span class="p">.</span><span class="nf">times</span><span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="no">Ractor</span><span class="p">.</span><span class="nf">new</span><span class="p">{</span> <span class="n">do_processing</span> <span class="p">}</span> <span class="p">}</span>
<span class="k">until</span> <span class="n">pool</span><span class="p">.</span><span class="nf">empty?</span> <span class="k">do</span>
<span class="n">ractor</span><span class="p">,</span> <span class="n">result</span> <span class="o">=</span> <span class="no">Ractor</span><span class="p">.</span><span class="nf">select</span><span class="p">(</span><span class="o">*</span><span class="n">pool</span><span class="p">)</span>
<span class="k">if</span> <span class="n">result</span> <span class="o">==</span> <span class="ss">:finish</span>
<span class="n">pool</span><span class="p">.</span><span class="nf">delete</span><span class="p">(</span><span class="n">ractor</span><span class="p">)</span>
<span class="k">else</span>
<span class="n">do_something_with</span><span class="p">(</span><span class="n">result</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>I would like to propose that it would be allowed (by default or at least via keyword parameter) to call <code>select</code> on terminated ractors, as long as there is at least one remaining open one.</p>
<p>This would make it very to resolve 1 and 2 above. Here's an example combine them both together:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">def</span> <span class="nf">do_processing</span>
<span class="nb">rand</span><span class="p">(</span><span class="mi">10</span><span class="p">).</span><span class="nf">times</span> <span class="k">do</span> <span class="p">{</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">yield</span> <span class="ss">:a_result</span>
<span class="p">}</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">current</span><span class="p">.</span><span class="nf">close</span> <span class="c1"># avoid yielding a value at the end</span>
<span class="k">end</span>
<span class="n">pool</span> <span class="o">=</span> <span class="mi">20</span><span class="p">.</span><span class="nf">times</span><span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="no">Ractor</span><span class="p">.</span><span class="nf">new</span><span class="p">{</span> <span class="n">do_processing</span> <span class="p">}</span> <span class="p">}.</span><span class="nf">freeze</span>
<span class="mi">5</span><span class="p">.</span><span class="nf">times</span> <span class="k">do</span> <span class="c1"># divide processing into 5 ractors</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">pool</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">pool</span><span class="o">|</span>
<span class="kp">loop</span> <span class="k">do</span>
<span class="n">_ractor</span><span class="p">,</span> <span class="n">result</span> <span class="o">=</span> <span class="no">Ractor</span><span class="p">.</span><span class="nf">select</span><span class="p">(</span><span class="o">*</span><span class="n">pool</span><span class="p">)</span> <span class="c1"># with my proposed lax select</span>
<span class="n">do_something_with</span><span class="p">(</span><span class="n">result</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>The <code>loop</code> above terminates when <code>Ractor.select</code> raises an error once the whole <code>pool</code> is terminated.</p>
<p>I'm new to actors but my intuition currently is that I will never want to take care of a pool of Ractors myself and would always prefer if <code>Ractor.select</code> did it for me. Are there use-cases where <code>Ractor.select</code> raising an error if it encounters a closed queue is helpful?</p>
<p>Notes:</p>
<ul>
<li>(*) <code>Ractor.select</code> doesn't really enforce ractors to be opened of course, it will work if the ractors are consumed in the right order, like in this example by chance:</li>
</ul>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="mi">10</span><span class="p">.</span><span class="nf">times</span><span class="p">.</span><span class="nf">map</span> <span class="k">do</span>
<span class="n">r</span> <span class="o">=</span> <span class="mi">2</span><span class="p">.</span><span class="nf">times</span><span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="no">Ractor</span><span class="p">.</span><span class="nf">new</span><span class="p">{</span> <span class="nb">sleep</span><span class="p">(</span><span class="mf">0.05</span><span class="p">);</span> <span class="ss">:ok</span> <span class="p">}</span> <span class="p">}</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">select</span><span class="p">(</span><span class="o">*</span><span class="n">r</span><span class="p">)</span> <span class="c1"># Get first available result</span>
<span class="c1"># Don't remove the ractor from `r`</span>
<span class="no">Ractor</span><span class="p">.</span><span class="nf">select</span><span class="p">(</span><span class="o">*</span><span class="n">r</span><span class="p">).</span><span class="nf">last</span> <span class="k">rescue</span> <span class="ss">:error</span> <span class="c1"># Get second result</span>
<span class="k">end</span>
<span class="c1"># => [:ok, :error, :error, :error, :error, :error, :error, :ok, :ok, :ok]</span>
</code></pre>
<ul>
<li>I think <code>Ractor.select(*pool, yield_value: 42)</code> would raise only if the current outgoing queue is closed, even if the whole pool was terminated</li>
<li>Similarly <code>Ractor.select(*pool, Ractor.current)</code> would raise only if the current incomming queue is also closed.</li>
</ul> Ruby master - Feature #17265 (Feedback): Add `Bool` modulehttps://bugs.ruby-lang.org/issues/172652020-10-19T00:11:59Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>1-line Summary: <code>rbs</code> would benefit from the existence of common ancestor <code>Bool</code> for <code>TrueClass</code> and <code>FalseClass</code>.</p>
<p>Detail:<br>
Matz: I am aware you rejected a similar request, but could we revisit this in light of RBS?</p>
<p>One use case was for an easy way to check for <code>true</code> or <code>false</code> values, instead of simply for truthiness (e.g. for data transfer, strict argument checking, testing, etc.)</p>
<p>I believe there's a new use case: <code>RBS</code></p>
<p>In <code>RBS</code>, the most used types like <code>String</code> and <code>Integer</code> have types for "string-like" and "integer-like" objects: <code>string</code> and <code>integer</code> (all lowercase).</p>
<p>For example the signature for <code>Integer#>></code> is:</p>
<pre><code>def >>: (int) -> Integer
</code></pre>
<p>It accepts an <code>Integer</code> <em>or an object responding to <code>to_int</code></em> (summarized by <code>int</code>) and returns an <code>Integer</code> (and never another class of object responding to <code>to_int</code> or not).</p>
<p>There is a similar idea with boolean values, where a method may accept any object and will use it's truthiness, while returning <code>true | false</code>. For example one of the interface for <code>Enumerable#all?</code> should look like:</p>
<pre><code>def all?: () { (Elem) -> bool } -> true | false
</code></pre>
<p>The user supplied block can return any value, and its truthiness (anything else than <code>nil</code> or <code>false</code>) will be used to determine the result of <code>all?</code>. That result will be <code>true | false</code>, and no other value.</p>
<p>If RBS is to be popular, there will be <em>many</em> signatures for such predicates (in builtin Ruby, stdlib, any gems, applications, etc.). I feel the best option would be <code>Bool</code>, if this would be reflected in Ruby itself.</p>
<p>Proposal: a new global module called <code>Bool</code>, without any method of constant, included in <code>TrueClass</code> and <code>FalseClass</code>.</p>
<p>Following reasons for rejection were given at the time:</p>
<blockquote>
<p>many gems and libraries had already introduced Boolean class. I don't want to break them.</p>
</blockquote>
<p>I looked and found the <a href="https://rubygems.org/gems/bool" class="external"><code>bool</code> gem</a> that defines a <code>Bool</code> module. My proposal is compatible. In any case, this gem looks abandoned, the author Aslak Hellesøy doesn't have the code on github, the gem has had 7000 downloads in the past 6 years and <a href="https://rubygems.org/gems/bool/reverse_dependencies" class="external">has no public reverse dependency</a>. It also fails to install on my machine.</p>
<p>I am not aware of incompatibilities.</p>
<blockquote>
<p><code>true</code> and <code>false</code> are the only representative of true-false values. In Ruby. <code>nil</code> and <code>false</code> are falsy values, and everything else is a true value. There's no meaning for having a superclass of <code>TrueClass</code> and <code>FalseClass</code> as <code>Boolean</code>.</p>
</blockquote>
<p>The proposal is exactly to be able to easily write about this duality of <code>Bool</code> as having only <code>true</code> and <code>false</code> as members, and every Ruby object as being implicitly convertible as being truthy or falsy (<code>bool</code> in RBS).</p>
<p>Discussion in RBS:</p>
<ul>
<li><a href="https://github.com/ruby/rbs/issues/133" class="external">https://github.com/ruby/rbs/issues/133</a></li>
</ul>
<p>Previous feature requests for <code>Boolean</code>:</p>
<ul>
<li><a href="https://bugs.ruby-lang.org/issues/14224" class="external">https://bugs.ruby-lang.org/issues/14224</a></li>
<li><a href="https://bugs.ruby-lang.org/issues/12515" class="external">https://bugs.ruby-lang.org/issues/12515</a></li>
</ul> Ruby master - Feature #17210 (Open): More readable and useful `Set#inspect`https://bugs.ruby-lang.org/issues/172102020-10-02T04:33:18Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>I would like to change <code>Set#inspect</code>/<code>to_s</code>:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># before</span>
<span class="nb">puts</span> <span class="no">Set</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="c1"># => "#<Set: {1, 2, 3}>"</span>
<span class="c1"># after</span>
<span class="nb">puts</span> <span class="no">Set</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="c1"># => "Set[1, 2, 3]"</span>
</code></pre>
<p>This output is shorter, readable, and has the property that it corresponds to Ruby code</p> Ruby master - Bug #17197 (Rejected): Some Hash methods still have arity 2 instead of 1https://bugs.ruby-lang.org/issues/171972020-09-28T02:13:57Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p><code>Hash#each</code> was changed recently to have arity of 1.<br>
All other methods of <code>Hash</code> should behave the same.<br>
Much has been fixed since <a class="issue tracker-1 status-5 priority-4 priority-default closed" title="Bug: Enumerable & Hash yielding arity (Closed)" href="https://bugs.ruby-lang.org/issues/14015">#14015</a>, but some remains:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># Some methods consistently have arity 2:</span>
<span class="p">{</span><span class="ss">a: </span><span class="mi">1</span><span class="p">}.</span><span class="nf">select</span><span class="p">(</span> <span class="o">&-></span><span class="p">(</span><span class="n">_kvp</span><span class="p">)</span> <span class="p">{}</span> <span class="p">)</span> <span class="c1"># => ArgumentError (wrong number of arguments (given 2, expected 1))</span>
</code></pre>
<p>All in all: <code>%i[select keep_if delete_if reject to_h]</code> have their arity still set at 2.</p> Ruby master - Feature #17171 (Rejected): Why is the visibility of constants not affected by `priv...https://bugs.ruby-lang.org/issues/171712020-09-15T20:34:44Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">Foo</span>
<span class="k">def</span> <span class="nf">call_me</span>
<span class="c1"># ...</span>
<span class="k">end</span>
<span class="kp">private</span>
<span class="no">SOME_DATA</span> <span class="o">=</span> <span class="sx">%i[...]</span><span class="p">.</span><span class="nf">freeze</span> <span class="c1"># is public, why not private?</span>
<span class="k">def</span> <span class="nf">calc_stuff</span> <span class="c1"># is private, ok.</span>
<span class="c1"># ...</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>It's probably a naive question, but why shouldn't <code>SOME_DATA</code>'s visibility be private?</p>
<p>When writing gems, more often than not the constants that I write are not meant for public consumption. I find it redundant (and tiresome) to explicitly write <code>private_constant :SOME_DATA</code>.</p> Ruby master - Feature #17145 (Rejected): Ractor-aware `Object#deep_freeze`https://bugs.ruby-lang.org/issues/171452020-09-03T18:42:27Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>I'd like to propose <code>Object#deep_freeze</code>:</p>
<p>Freezes recursively the contents of the receiver (by calling <code>deep_freeze</code>) and<br>
then the receiver itself (by calling <code>freeze</code>).<br>
Values that are shareable via <code>Ractor</code> (e.g. classes) are never frozen this way.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># freezes recursively:</span>
<span class="n">ast</span> <span class="o">=</span> <span class="p">[</span><span class="ss">:hash</span><span class="p">,</span> <span class="p">[</span><span class="ss">:pair</span><span class="p">,</span> <span class="p">[</span><span class="ss">:str</span><span class="p">,</span> <span class="s1">'hello'</span><span class="p">],</span> <span class="p">[</span><span class="ss">:sym</span><span class="p">,</span> <span class="ss">:world</span><span class="p">]]].</span><span class="nf">deep_freeze</span>
<span class="n">ast</span><span class="p">.</span><span class="nf">dig</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="c1"># => [:str, 'hello']</span>
<span class="n">ast</span><span class="p">.</span><span class="nf">dig</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">).</span><span class="nf">compact!</span> <span class="c1"># => FrozenError</span>
<span class="c1"># does not freeze classes:</span>
<span class="p">[[</span><span class="no">String</span><span class="p">]].</span><span class="nf">deep_freeze</span>
<span class="no">String</span><span class="p">.</span><span class="nf">frozen?</span> <span class="c1"># => false</span>
<span class="c1"># calls `freeze`:</span>
<span class="k">class</span> <span class="nc">Foo</span>
<span class="k">def</span> <span class="nf">freeze</span>
<span class="n">build_cache!</span>
<span class="nb">puts</span> <span class="s2">"Ready for freeze"</span>
<span class="k">super</span>
<span class="k">end</span>
<span class="c1"># ...</span>
<span class="k">end</span>
<span class="p">[[[</span><span class="no">Foo</span><span class="p">.</span><span class="nf">new</span><span class="p">]]].</span><span class="nf">deep_freeze</span> <span class="c1"># => Outputs "Ready for freeze"</span>
</code></pre>
<p>I think a variant <code>deep_freeze!</code> that raises an exception if the result isn't Ractor-shareable would be useful too:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">Fire</span>
<span class="k">def</span> <span class="nf">freeze</span>
<span class="c1"># do not call super</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="no">Fire</span><span class="p">.</span><span class="nf">new</span><span class="p">]</span>
<span class="n">x</span><span class="p">.</span><span class="nf">deep_freeze!</span> <span class="c1"># => "Could not be deeply-frozen: #<Fire:0x00007ff151994748>"</span>
</code></pre> Ruby master - Bug #17124 (Closed): Wrong "ambiguous first argument" warninghttps://bugs.ruby-lang.org/issues/171242020-08-18T20:02:54Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<pre><code class="sh syntaxhl" data-language="sh"><span class="nv">$ </span>ruby <span class="nt">-v</span> <span class="nt">-e</span> <span class="s2">"x='a'; x.match? /[a-z]/"</span>
ruby 2.8.0dev <span class="o">(</span>2020-07-30T14:07:06Z master 352895b751<span class="o">)</span> <span class="o">[</span>x86_64-darwin18]
<span class="nt">-e</span>:1: warning: ambiguous first argument<span class="p">;</span> put parentheses or a space even after <span class="sb">`</span>/<span class="s1">' operator
</span></code></pre>
<p>There is no <code>/</code> operator in there and there is also no ambiguity as adding a space after the first <code>/</code> is a syntax error.</p>
<p>Is it possible to remove the warning altogether when the argument is lexed as a regexp?</p>
<p>The message could use a rewording too, maybe "ambiguous first argument; put parentheses around argument or add a space after `/' operator"</p> Ruby master - Bug #17092 (Closed): Array#flatten with finite depth should flatten recursive arrayshttps://bugs.ruby-lang.org/issues/170922020-07-30T12:29:24Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Recursive arrays can not be flattened currently:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">a</span> <span class="o">=</span> <span class="p">[];</span> <span class="n">a</span> <span class="o"><<</span> <span class="n">a</span>
<span class="n">a</span><span class="p">.</span><span class="nf">flatten</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="c1"># => tried to flatten recursive array</span>
</code></pre>
<p>The only valid reason to raise an error for recursive arrays is for flatten with no argument (or negative argument); the case for finite-depth flatten is not problematic.</p>
<p>This fix has the bonus of speeding up the finite-depth case in general.</p>
<p>I will merge <a href="https://github.com/ruby/ruby/pull/3374" class="external">https://github.com/ruby/ruby/pull/3374</a>, <a class="user active user-mention" href="https://bugs.ruby-lang.org/users/4">@nobu (Nobuyoshi Nakada)</a> asked for backports.</p> Ruby master - Bug #17031 (Closed): `Kernel#caller_locations(m, n)` should be optimizedhttps://bugs.ruby-lang.org/issues/170312020-07-14T19:07:29Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p><code>Kernel#caller_locations(1, 1)</code> currently appears to needlessly allocate memory for the whole backtrace.</p>
<p>It allocates ~20kB for a 800-deep stacktrace, vs 1.6 kB for a shallow backtrace.<br>
It is also much slower for long stacktraces: about 7x slower for a 800-deep backtrace than for a shallow one.</p>
<p>Test used:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">def</span> <span class="nf">do_something</span>
<span class="n">location</span> <span class="o">=</span> <span class="n">caller_locations</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">).</span><span class="nf">first</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">test</span><span class="p">(</span><span class="n">depth</span><span class="p">,</span> <span class="n">trigger</span><span class="p">)</span>
<span class="n">do_something</span> <span class="k">if</span> <span class="n">depth</span> <span class="o">==</span> <span class="n">trigger</span>
<span class="nb">test</span><span class="p">(</span><span class="n">depth</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="n">trigger</span><span class="p">)</span> <span class="k">unless</span> <span class="n">depth</span> <span class="o">==</span> <span class="mi">0</span>
<span class="k">end</span>
<span class="nb">require</span> <span class="s1">'benchmark/ips'</span>
<span class="no">Benchmark</span><span class="p">.</span><span class="nf">ips</span> <span class="k">do</span> <span class="o">|</span><span class="n">x</span><span class="o">|</span>
<span class="n">x</span><span class="p">.</span><span class="nf">report</span> <span class="p">(</span><span class="ss">:short_backtrace</span> <span class="p">)</span> <span class="p">{</span><span class="nb">test</span><span class="p">(</span><span class="mi">800</span><span class="p">,</span><span class="mi">800</span><span class="p">)}</span>
<span class="n">x</span><span class="p">.</span><span class="nf">report</span> <span class="p">(</span><span class="ss">:long_backtrace</span> <span class="p">)</span> <span class="p">{</span><span class="nb">test</span><span class="p">(</span><span class="mi">800</span><span class="p">,</span> <span class="mi">0</span><span class="p">)}</span>
<span class="n">x</span><span class="p">.</span><span class="nf">report</span> <span class="p">(</span><span class="ss">:no_caller_locations</span><span class="p">)</span> <span class="p">{</span><span class="nb">test</span><span class="p">(</span><span class="mi">800</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">)}</span>
<span class="k">end</span>
<span class="nb">require</span> <span class="s1">'memory_profiler'</span>
<span class="no">MemoryProfiler</span><span class="p">.</span><span class="nf">report</span> <span class="p">{</span> <span class="nb">test</span><span class="p">(</span><span class="mi">800</span><span class="p">,</span><span class="mi">800</span><span class="p">)</span> <span class="p">}.</span><span class="nf">pretty_print</span><span class="p">(</span><span class="ss">scale_bytes: </span><span class="kp">true</span><span class="p">,</span> <span class="ss">detailed_report: </span><span class="kp">false</span><span class="p">)</span>
<span class="no">MemoryProfiler</span><span class="p">.</span><span class="nf">report</span> <span class="p">{</span> <span class="nb">test</span><span class="p">(</span><span class="mi">800</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="p">}.</span><span class="nf">pretty_print</span><span class="p">(</span><span class="ss">scale_bytes: </span><span class="kp">true</span><span class="p">,</span> <span class="ss">detailed_report: </span><span class="kp">false</span><span class="p">)</span>
</code></pre>
<p>Found when checking memory usage on RuboCop.</p> Ruby master - Bug #17030 (Closed): Enumerable#grep{_v} should be optimized for Regexphttps://bugs.ruby-lang.org/issues/170302020-07-13T20:26:50Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Currently,</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">array</span><span class="p">.</span><span class="nf">select</span> <span class="p">{</span> <span class="o">|</span><span class="n">e</span><span class="o">|</span> <span class="n">e</span><span class="p">.</span><span class="nf">match?</span><span class="p">(</span><span class="no">REGEXP</span><span class="p">)</span> <span class="p">}</span>
</code></pre>
<p>is about three times faster and six times more memory efficient than</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">array</span><span class="p">.</span><span class="nf">grep</span><span class="p">(</span><span class="no">REGEXP</span><span class="p">)</span>
</code></pre>
<p>This is because <code>grep</code> calls <code>Regexp#===</code>, which creates useless <code>MatchData</code>.</p> Ruby master - Bug #16996 (Closed): Hash should avoid doing unnecessary rehashhttps://bugs.ruby-lang.org/issues/169962020-06-27T08:20:32Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Pop quiz: Which is the fastest way to get a copy of a Hash <code>h</code>?</p>
<p>If, like me, you thought <code>h.dup</code> (of course, right?), you are actually wrong.</p>
<p>The fastest way is to call <code>h.merge</code>. Try it:</p>
<pre><code>require 'benchmark/ips'
lengths = 1..50
h = lengths.to_h { |i| ['x' * i, nil] }
Benchmark.ips do |x|
x.report("dup") { h.dup }
x.report("merge") { h.merge }
end
</code></pre>
<p>I get</p>
<pre><code>Calculating -------------------------------------
dup 259.233k (± 9.2%) i/s - 1.285M in 5.013445s
merge 944.095k (± 8.2%) i/s - 4.693M in 5.005315s
</code></pre>
<p>Yup, it's <em>3.5x faster</em> with this example!!</p>
<p>Why? Because <code>Hash#dup</code> does a rehash, and <code>merge</code> does not.</p>
<p>Pop quiz 2: which methods of <code>Hash</code> that produce a new hash do a rehash?</p>
<p>Answer: it depends on the method and on the Ruby version</p>
<pre><code>
+---------------------------------+------+-----+-----+-----+-----+-----+-----+-----+-----+
| Does this rehash? | head | 2.7 | 2.6 | 2.5 | 2.4 | 2.3 | 2.2 | 2.1 | 2.0 |
+---------------------------------+------+-----+-----+-----+-----+-----+-----+-----+-----+
| h.dup / clone | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
+---------------------------------+------+-----+-----+-----+-----+-----+-----+-----+-----+
| h.select{true} / reject{false} | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
+---------------------------------+------+-----+-----+-----+-----+-----+-----+-----+-----+
| h.select!{true} / reject!{false}| Ø | Ø | Ø | Ø | Ø | Ø | Ø | Ø | Ø |
+---------------------------------+------+-----+-----+-----+-----+-----+-----+-----+-----+
| sub_h.to_h | Ø | Ø | Ø | Ø | Ø | Ø | Ø | Ø | Ø |
+---------------------------------+------+-----+-----+-----+-----+-----+-----+-----+-----+
| h.merge({}) | Ø | Ø | Ø | Ø | Yes | Yes | Yes | Yes | Yes |
+---------------------------------+------+-----+-----+-----+-----+-----+-----+-----+-----+
| h.merge | Ø | Ø | Ø | n/a |
+---------------------------------+------+-----+-----+-----+-----+-----+-----+-----+-----+
| h.transform_values(&:itself) | Ø | Ø | Yes | Yes | Yes | n/a |
+---------------------------------+------+-----+-----+-----+-----+-----+-----+-----+-----+
(where `sub_h = Class.new(Hash).replace(h)`, Ø = no rehash)
</code></pre>
<p>So in Ruby head, doing <code>h.merge({})</code> or even <code>h.transform_values(&:itself)</code> will be much faster than <code>h.dup</code> (but slower in Ruby 2.4) (*)</p>
<p>Notice that <code>select</code> rehashes, but <code>select!</code> doesn't, so the fastest way to do a <code>select</code> in Ruby is... not to call select and instead to actually do a <code>merge.select!</code>! (*)</p>
<p>*: on hashes with non-negligible hash functions</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">fast_select</span><span class="p">(</span><span class="o">&</span><span class="n">block</span><span class="p">)</span>
<span class="n">merge</span><span class="p">.</span><span class="nf">select!</span><span class="p">(</span><span class="o">&</span><span class="n">block</span><span class="p">)</span> <span class="c1"># don't call dup because it's slow</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="no">Benchmark</span><span class="p">.</span><span class="nf">ips</span> <span class="k">do</span> <span class="o">|</span><span class="n">x</span><span class="o">|</span>
<span class="n">x</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s2">"select"</span><span class="p">)</span> <span class="p">{</span> <span class="n">h</span><span class="p">.</span><span class="nf">select</span><span class="p">{</span><span class="kp">true</span><span class="p">}</span> <span class="p">}</span>
<span class="n">x</span><span class="p">.</span><span class="nf">report</span><span class="p">(</span><span class="s2">"fast_select"</span><span class="p">)</span> <span class="p">{</span> <span class="n">h</span><span class="p">.</span><span class="nf">fast_select</span><span class="p">{</span><span class="kp">true</span><span class="p">}</span> <span class="p">}</span>
<span class="k">end</span>
</code></pre>
<p>On my test case above, <code>fast_select</code> is <em>2.5x faster</em> than <code>select</code>. <code>fast_select</code> will always return exactly the same result (unless the receiver needed a rehash).</p>
<p>Pop quiz 3: Is this a bug or a feature?</p>
<p>It should be clear that no feature of Ruby should be re-implementable in Ruby with a 3.5x / 2.5x speed gain, so many would think "of course it's a bug".</p>
<p>Well, <a href="https://bugs.ruby-lang.org/issues/16121" class="external">https://bugs.ruby-lang.org/issues/16121</a> seems to think that <code>Hash#dup</code>'s rehash is a feature...<br>
Why?<br>
Because there is actually a test that <code>dup</code> does a rehash<br>
Why?<br>
Because a test of <code>Set</code> was failing otherwise!<br>
Commit: <a href="https://github.com/ruby/ruby/commit/a34a3c2caae4c1fbd" class="external">https://github.com/ruby/ruby/commit/a34a3c2caae4c1fbd</a><br>
Short discussion: <a href="http://blade.nagaokaut.ac.jp/cgi-bin/vframe.rb/ruby/ruby-core/48040?47945-48527" class="external">http://blade.nagaokaut.ac.jp/cgi-bin/vframe.rb/ruby/ruby-core/48040?47945-48527</a><br>
Actual test: <a href="https://github.com/ruby/ruby/blob/master/test/test_set.rb#L621-L625" class="external">https://github.com/ruby/ruby/blob/master/test/test_set.rb#L621-L625</a><br>
Why?<br>
This test construct a <code>Set</code> that needs to be rehashed (by mutating an element of the set after it is added), and then checks that <code>rehash_me == rehash_me.clone</code>.<br>
That test is bogus. It passes for obscure and undocumented reasons, and <code>rehash_me.clone == rehash_me</code> doesn't pass.<br>
Today, it is official that sets with elements that are later mutated must be <code>Set#reset</code>, so it is official that this should not be relied upon.</p>
<p>Probably more clear is the case of <code>select/reject</code> (but I didn't check for failing test), and even more clear that <code>merge</code> changed in Ruby 2.5 and <code>transform_values</code> in 2.7, but not a single <code>NEWS</code> file mentions the word "rehash".</p>
<p>My conclusion is that Hash should avoid doing an unnecessary rehash: <code>dup</code>/<code>clone</code>/<code>select</code>/<code>reject</code>. We probably should add a reminder in the <code>NEWS</code> that if anyone mutates a key of a Hash, or an element of a Set and does not call <code>rehash</code>/<code>reset</code>, improper behavior should be expected.</p>
<p>Let's make <code>Hash#dup/clone/select/reject</code> fast please.</p>
<p>Any objection?</p> Ruby master - Feature #16995 (Closed): Sets: <=> should be specializedhttps://bugs.ruby-lang.org/issues/169952020-06-26T20:43:58Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>This is quite minor, but <code>Set#<=></code> should be refined.</p>
<p>Reminder: <code>Set</code> defines <code><</code>, <code>></code>, etc. as inclusion, but does not have a corresponding <code><=></code>:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">Set</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o"><</span> <span class="no">Set</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"># => true</span>
<span class="no">Set</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o"><=></span> <span class="no">Set</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"># => nil, should be -1</span>
<span class="no">Set</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o"><=></span> <span class="no">Set</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="c1"># => nil, ok, not orderable</span>
</code></pre>
<p>The official stated reason for <code>Set</code> to <em>not</em> implement is that some sets are not comparable. That is exactly what <code>nil</code> result type is for IMO. Sets are partically ordered and <code><=></code> should reflect that. <a href="https://en.wikipedia.org/wiki/Partially_ordered_set" class="external">https://en.wikipedia.org/wiki/Partially_ordered_set</a></p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">Set</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o"><</span> <span class="no">Set</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"># => true</span>
<span class="p">[</span><span class="no">Set</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="no">Set</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="nf">sort</span> <span class="c1"># => ArgumentError, should be [Set[1], Set[1, 2]]</span>
<span class="p">[</span><span class="no">Set</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="no">Set</span><span class="p">[</span><span class="mi">2</span><span class="p">]].</span><span class="nf">sort</span> <span class="c1"># => ArgumentError, ok, can't be ordered</span>
</code></pre>
<p>This is <em>exactly the same</em> idea as <code>Class</code>, which correctly refines <code><=></code>:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">Array</span> <span class="o"><</span> <span class="no">Enumerable</span> <span class="c1"># => true</span>
<span class="no">Array</span> <span class="o"><=></span> <span class="no">Enumerable</span> <span class="c1"># => -1, ok</span>
<span class="p">[</span><span class="no">Array</span><span class="p">,</span> <span class="no">Enumerable</span><span class="p">].</span><span class="nf">sort</span> <span class="c1"># => [Array, Enumerable]</span>
<span class="p">[</span><span class="no">Array</span><span class="p">,</span> <span class="no">String</span><span class="p">].</span><span class="nf">sort</span> <span class="c1"># => ArgumentError (comparison of Class with Class failed), ok</span>
</code></pre> Ruby master - Feature #16994 (Feedback): Sets: shorthand for frozen sets of symbols / stringshttps://bugs.ruby-lang.org/issues/169942020-06-26T20:32:22Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>I would like a shorthand syntax for <em>frozen Sets of symbols or of strings</em>.</p>
<p>I am thinking of:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="o">%</span><span class="n">ws</span><span class="p">{</span><span class="n">hello</span> <span class="n">world</span><span class="p">}</span> <span class="c1"># => Set['hello', 'world'].freeze</span>
<span class="o">%</span><span class="n">is</span><span class="p">{</span><span class="n">hello</span> <span class="n">world</span><span class="p">}</span> <span class="c1"># => Set[:hello, :world].freeze</span>
</code></pre>
<p>The individual strings would be frozen. These literals would be created once at parse time (like Regex are):</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">def</span> <span class="nf">foo</span>
<span class="nb">p</span> <span class="o">%</span><span class="n">ws</span><span class="p">{</span><span class="n">hello</span> <span class="n">world</span><span class="p">}.</span><span class="nf">object_id</span>
<span class="k">end</span>
<span class="n">foo</span>
<span class="n">foo</span> <span class="c1"># => prints the same id twice</span>
</code></pre>
<p>We should consider these sets to return a unique frozen <code>to_a</code>.</p>
<p>Reminder: Ruby has literal notations for <code>Rational</code> and <code>Complex</code>. I've sadly never had to use either.<br>
I would venture to say that <code>Complex</code> is much less used than <code>Sets</code>, and that sets are underused.</p>
<p>Reminder: previous discussion for builtin syntax was not for frozen literal, strings or symbols specifically: <a href="https://bugs.ruby-lang.org/issues/5478" class="external">https://bugs.ruby-lang.org/issues/5478</a></p>
<p>For builtin notations for generic sets (i.e. <em>unfrozen</em> or containing <em>other than string/symbol</em>), please discuss in another issue.</p> Ruby master - Feature #16993 (Open): Sets: from hash keys using Hash#key_sethttps://bugs.ruby-lang.org/issues/169932020-06-26T20:31:27Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>To create a set from hash keys currently implies a temporary array for all keys, rehashing all those keys and rebuilding a hash. Instead, the hash could be copied and its values set to <code>true</code>.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">h</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="c1"># Now:</span>
<span class="no">Set</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">h</span><span class="p">.</span><span class="nf">keys</span><span class="p">)</span> <span class="c1"># => Set[:a]</span>
<span class="c1"># After</span>
<span class="n">h</span><span class="p">.</span><span class="nf">key_set</span> <span class="c1"># => Set[:a], efficiently.</span>
</code></pre> Ruby master - Feature #16992 (Open): Sets: officially orderedhttps://bugs.ruby-lang.org/issues/169922020-06-26T20:30:50Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Officially, set elements have uncertain order. This predades when Hash started being ordered (Ruby 1.9.0, Xmas 2007). Sets have since been de-facto insertion-ordered. FYI, in those 13 years, there have been about 70 commits to <code>lib/set.rb</code>.</p>
<p>I have the impression that a non-negligible amount of code in the wild rely on sets being ordered, at least under most circumstances. I feel that this should be officialized.</p>
<p>If sets are truly unordered, then why do we hesitate to make an optimization of <code>&</code> and <code>|</code>: <a href="https://bugs.ruby-lang.org/issues/15281" class="external">https://bugs.ruby-lang.org/issues/15281</a></p>
<p>See also: <a href="https://bugs.ruby-lang.org/issues/14069" class="external">https://bugs.ruby-lang.org/issues/14069</a></p> Ruby master - Feature #16991 (Closed): Sets: add Set#joinhttps://bugs.ruby-lang.org/issues/169912020-06-26T20:29:01Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>I'd like to add <code>#join</code> to <code>Set</code>:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># Now:</span>
<span class="no">Set</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="nf">join</span><span class="p">(</span><span class="s1">'x'</span><span class="p">)</span> <span class="c1"># => NoMethodError</span>
<span class="c1"># After</span>
<span class="no">Set</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="nf">join</span><span class="p">(</span><span class="s1">'x'</span><span class="p">)</span> <span class="c1"># => "1x2x3"</span>
</code></pre>
<p>I'd make this join never recursive. (I've never wanted to use the recursivity of <code>Array#join</code>, but it may very well just be my particular experience)</p>
<p>Reminder: Why there is no <code>Enumerable#join</code>: <a href="https://bugs.ruby-lang.org/issues/1893" class="external">https://bugs.ruby-lang.org/issues/1893</a></p> Ruby master - Feature #16990 (Open): Sets: operators compatibility with Arrayhttps://bugs.ruby-lang.org/issues/169902020-06-26T20:27:17Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>We currently have <code>set <operator> array</code> work fine:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">Set</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="c1"># => Set[1, 2]</span>
</code></pre>
<p>Nothing works in the reverse order:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="no">Set</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="c1"># => no implicit conversion of Set into Array</span>
<span class="c1"># should be:</span>
<span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="no">Set</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="c1"># => [1, 2]</span>
</code></pre>
<a name="set-like-operators"></a>
<h4 >set-like operators<a href="#set-like-operators" class="wiki-anchor">¶</a></h4>
<p>Note that the situation is particularly frustrating for <code>&</code>, <code>|</code> and <code>-</code>.<br>
If someone wants to do <code>ary - set</code>, one <strong>has</strong> to do <code>ary - set.to_a</code> which will, internally, do a <code>to_set</code>, so what is happening is <code>set.to_a.to_set</code>!! (assuming <code>ary</code> is over <code>SMALL_ARRAY_LEN == 16</code> size, otherwise it's still doing in <code>O(ary * set)</code> instead of <code>O(ary)</code>).</p>
<p>The same holds with <code>&</code> and <code>|</code>; see order issue as to why this can <em>not</em> (officially) be done any other way.</p>
<p>Reminder:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">ary</span> <span class="o">&</span> <span class="n">ary</span><span class="p">.</span><span class="nf">reverse</span> <span class="c1"># => ary</span>
<span class="no">Set</span><span class="p">[</span><span class="o">*</span><span class="n">ary</span><span class="p">]</span> <span class="o">&</span> <span class="no">Set</span><span class="p">[</span><span class="o">*</span><span class="n">ary</span><span class="p">.</span><span class="nf">reverse</span><span class="p">]</span> <span class="c1"># => Set[*ary.reverse], officially order is indeterminate</span>
</code></pre> Ruby master - Feature #16989 (Open): Sets: need ♥️https://bugs.ruby-lang.org/issues/169892020-06-26T20:18:17Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>I am opening a series of feature requests on <code>Set</code>, all of them based on this usecase.</p>
<p>The main usecase I have in mind is my recent experience with <code>RuboCop</code>. I noticed a big number of frozen arrays being used only to later call <code>include?</code> on them. This is <code>O(n)</code> instead of <code>O(1)</code>.</p>
<p>Trying to convert them to <code>Set</code>s causes major compatibility issues, as well as very frustrating situations and some cases that would make them much less efficient.</p>
<p>Because of these incompatibilities, <code>RuboCop</code> is in the process of using a custom class based on <code>Array</code> with optimized <code>include?</code> and <code>===</code>. <code>RuboCop</code> runs multiple checks on Ruby code. Those checks are called cops. <code>RuboCop</code> performance is (IMO) pretty bad and some cops currently are in <code>O(n^2)</code> where n is the size of the code being inspected. Even given these extremely inefficient cops, optimizing the 100+ such arrays (most of which are quite small btw) gave a 5% speed boost.</p>
<p>RuboCop PRs for reference: <a href="https://github.com/rubocop-hq/rubocop-ast/pull/29" class="external">https://github.com/rubocop-hq/rubocop-ast/pull/29</a><br>
<a href="https://github.com/rubocop-hq/rubocop/pull/8133" class="external">https://github.com/rubocop-hq/rubocop/pull/8133</a></p>
<p>My experience tells me that there are many other opportunities to use <code>Set</code>s that are missed because <code>Set</code>s are not builtin, not known enough and have no shorthand notation.</p>
<p>In this issue I'd like to concentrate the discussion on the following request: <code>Set</code>s should be core objects, in the same way that <code>Complex</code> were not and are now. Some of the upcoming feature requests would be easier (or only possible) to implement were <code>Set</code>s builtin.</p> Ruby master - Feature #16985 (Open): Improve `pp` for `Hash` and `String`https://bugs.ruby-lang.org/issues/169852020-06-25T17:28:06Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Could we improve <code>pp</code> for <code>Hash</code> and <code>String</code>:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">pp</span><span class="p">({</span><span class="ss">hello: </span><span class="s1">'My name is "Marc-André"'</span><span class="p">})</span>
<span class="c1"># =></span>
<span class="p">{</span><span class="ss">hello: </span><span class="s1">'My name is "Marc-André"'</span><span class="p">}</span>
<span class="c1"># instead of</span>
<span class="p">{</span><span class="ss">:hello</span><span class="o">=></span><span class="s2">"My name is </span><span class="se">\"</span><span class="s2">Marc-André</span><span class="se">\"</span><span class="s2">"</span><span class="p">}</span>
</code></pre>
<p>If any key is non-symbol, they would continue to be output as <code><key> => <value></code>. If a string contains single quotes, or characters that need escaping (e.g. <code>"\n"</code>), current format would be used.</p>
<p>I'll gladly provide a PR if this is deemed acceptable.</p>
<p>I would even like this for <code>String#inspect</code> and <code>Hash#inspect</code> but it's not clear if this could lead to much incompatibility (maybe test suites?)</p> Ruby master - Bug #16281 (Closed): `irb -w` issues warninghttps://bugs.ruby-lang.org/issues/162812019-10-25T20:39:07Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<pre><code>$ irb -w
.rvm/rubies/ruby-head/lib/ruby/2.7.0/reline.rb:322: warning: instance variable @ambiguous_width not initialized
</code></pre>
<p>(Since f13db4adde532)</p> Ruby master - Feature #15918 (Open): Pattern matching for Sethttps://bugs.ruby-lang.org/issues/159182019-06-12T13:44:57Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Currently, <code>Set</code> does not respond to <code>deconstruct</code>. Shouldn't we implement it using <code>to_a</code>?</p>
<pre><code>require 'set'
case Set[1, 2, 3]
in [1, 2, 3]
p "match"
else
p "no match"
end
# => "no match", should be "match"
</code></pre> Ruby master - Feature #15917 (Closed): Pattern matching for Structhttps://bugs.ruby-lang.org/issues/159172019-06-12T13:41:00Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p><code>Struct</code>s respond to <code>deconstruct</code>, not to <code>deconstruct_keys</code>. Shouldn't we also implement it?</p>
<pre><code>A = Struct.new(:foo, :bar)
case A.new(1, 2)
in foo: 1, **rest
p "match"
else
p "no match"
end
# => "no match", should be "match"
</code></pre> Ruby master - Feature #15881 (Open): Optimize deconstruct in pattern matchinghttps://bugs.ruby-lang.org/issues/158812019-05-27T16:19:35Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">A</span>
<span class="k">def</span> <span class="nf">deconstruct</span>
<span class="nb">puts</span> <span class="s1">'deconstruct called'</span>
<span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">case</span> <span class="no">A</span><span class="p">.</span><span class="nf">new</span>
<span class="k">in</span> <span class="p">[</span><span class="mi">2</span><span class="p">]</span>
<span class="mi">2</span>
<span class="k">in</span> <span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="mi">1</span>
<span class="k">else</span>
<span class="k">end</span>
</code></pre>
<p>Currently this outputs:</p>
<pre><code>deconstruct called
deconstruct called
=> 1
</code></pre>
<p>Shouldn't <code>deconstruct called</code> print only once, whenever the first deconstruction needed occurs?</p> Ruby master - Bug #15731 (Closed): Wrong evaluation of many keyword default arguments in 2.3 - 2.5https://bugs.ruby-lang.org/issues/157312019-03-27T17:53:16Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>I don't know if it's worth fixing at this point, but we found a strange bug with evaluation of default keyword arguments when there are many of them (more than 32).</p>
<pre><code>def foo(
k0: puts(0), k1: puts(1), k2: puts(2), k3: puts(3), k4: puts(4),
k5: puts(5), k6: puts(6), k7: puts(7), k8: puts(8), k9: puts(9),
k10: puts(10), k11: puts(11), k12: puts(12), k13: puts(13), k14: puts(14),
k15: puts(15), k16: puts(16), k17: puts(17), k18: puts(18), k19: puts(19),
k20: puts(20), k21: puts(21), k22: puts(22), k23: puts(23), k24: puts(24),
k25: puts(25), k26: puts(26), k27: puts(27), k28: puts(28), k29: puts(29),
k30: puts(30), k31: puts(31), k32: puts(32), k33: puts(33)
)
k33
end
puts "No params:"
foo # Should print 1 to 33
puts "Only k33 param:"
foo(k33: 1) # Should print 1 to 32
puts "Only k32 and k33 params:"
r = foo(k32: 1, k33: 1) # Should print 1 to 31 and return 1
puts "Result: #{r.inspect}"
</code></pre>
<p>Ruby 2.4:<br>
last case is wrong. It prints 1 to 33 instead of 1 to 31 and returns <code>nil</code> instead of 1.</p>
<p>Ruby 2.5:<br>
same result for last case<br>
first two cases evaluates the default exactly the wrong parameters: those that are given and not for those not given. So it prints nothing and 33 respectively, instead of 1 to 33 and 1 to 32!</p>
<p>Ruby 2.6:<br>
results are ok</p>
<p>This strange behavior disappears with fewer keyword arguments.</p> Ruby master - Bug #15718 (Closed): YAML raises error when dumping strings with UTF32 encodinghttps://bugs.ruby-lang.org/issues/157182019-03-20T20:21:55Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<pre><code class="shell syntaxhl" data-language="shell">ruby <span class="nt">-r</span> yaml <span class="nt">-e</span> <span class="s2">"p YAML.dump( ''.force_encoding('UTF-32LE') )"</span>
Traceback <span class="o">(</span>most recent call last<span class="o">)</span>:
4: from <span class="nt">-e</span>:1:in <span class="sb">`</span><main><span class="s1">'
3: from /Users/work/.rvm/rubies/ruby-2.6.1/lib/ruby/2.6.0/psych.rb:513:in `dump'</span>
2: from /Users/work/.rvm/rubies/ruby-2.6.1/lib/ruby/2.6.0/psych/visitors/yaml_tree.rb:118:in <span class="sb">`</span>push<span class="s1">'
1: from /Users/work/.rvm/rubies/ruby-2.6.1/lib/ruby/2.6.0/psych/visitors/yaml_tree.rb:136:in `accept'</span>
/Users/work/.rvm/rubies/ruby-2.6.1/lib/ruby/2.6.0/psych/visitors/yaml_tree.rb:298:in <span class="sb">`</span>visit_String<span class="s1">': incompatible encoding regexp match (US-ASCII regexp with UTF-32LE string) (Encoding::CompatibilityError)
</span></code></pre>
<p>Surprisingly, this works in Ruby 2.4.x, but not in 2.2, 2.3, 2.5 nor 2.6!</p> Ruby master - Bug #15613 (Closed): Enumerator::Chain#each doesn't relay block signaturehttps://bugs.ruby-lang.org/issues/156132019-02-20T04:29:41Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Currently, the block given when iterating on the components of a <code>Enumerator::Chain</code> always have arity of -1 and <code>lambda?</code> is always <code>false</code>:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">Foo</span>
<span class="kp">include</span> <span class="no">Enumerable</span>
<span class="k">def</span> <span class="nf">each</span><span class="p">(</span><span class="o">&</span><span class="n">block</span><span class="p">)</span>
<span class="k">return</span> <span class="n">to_enum</span> <span class="k">unless</span> <span class="n">block</span>
<span class="nb">p</span> <span class="n">block</span><span class="p">.</span><span class="nf">arity</span><span class="p">,</span> <span class="n">block</span><span class="p">.</span><span class="nf">lambda?</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">foo</span> <span class="o">=</span> <span class="no">Foo</span><span class="p">.</span><span class="nf">new</span>
<span class="n">foo</span><span class="p">.</span><span class="nf">each</span><span class="p">(</span><span class="o">&-></span><span class="p">{})</span> <span class="c1"># => 0, true</span>
<span class="n">foo</span><span class="p">.</span><span class="nf">each</span><span class="p">.</span><span class="nf">each</span><span class="p">(</span><span class="o">&-></span><span class="p">{})</span> <span class="c1"># => 0, true</span>
<span class="n">foo</span><span class="p">.</span><span class="nf">chain</span><span class="p">.</span><span class="nf">each</span><span class="p">(</span><span class="o">&-></span><span class="p">{})</span> <span class="c1"># => -1, false. Would ideally be 0, true</span>
</code></pre>
<p>It would be preferable if the block information wasn't lost.</p> Ruby master - Bug #15376 (Closed): Default gems: how will it work exactly?https://bugs.ruby-lang.org/issues/153762018-12-04T06:38:35Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Some standard libraries will be gemified in Ruby 2.6+.</p>
<ol>
<li>
<p>I believe we need to provide documentation for this. The NEWS file doesn't have much at all. The respective READMEs also have no relevant information and are misleading (see <a href="https://github.com/ruby/ostruct/pull/6" class="external">https://github.com/ruby/ostruct/pull/6</a>). I'll be glad to write an initial draft if need be, with the answers to the following questions...</p>
</li>
<li>
<p>Can we play with this right now? Maybe publishing prelease versions of these libraries?</p>
</li>
<li>
<p>Has this been tested with <code>Gemfile</code> and <code>gemspec</code>, i.e. it will be possible to add a specification for one of these?</p>
</li>
<li>
<p>What is supposed to happen if one does <code>gem install ostruct</code> in Ruby 2.5 (with current bundler, and with future bundler)?</p>
</li>
<li>
<p>Will it be possible to use these in <code>Gemfile</code>s even with older Ruby (but recent <code>bundler</code>), so one could say <code>gem 'ostruct'</code> in a Gemfile and run <code>bundle install</code> in Ruby 2.5 without things exploding?</p>
</li>
<li>
<p>Depending on 4/5, shouldn't we specify a <code> required_rubygems_version</code> and/or <code> required_ruby_version</code> in the gemspecs?</p>
</li>
</ol> Ruby master - Bug #15332 (Closed): coverage and InstructionSequence regressionhttps://bugs.ruby-lang.org/issues/153322018-11-22T23:31:26Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>With the current head:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"> <span class="nb">require</span> <span class="s1">'coverage'</span>
<span class="no">Coverage</span><span class="p">.</span><span class="nf">start</span>
<span class="no">RubyVM</span><span class="o">::</span><span class="no">InstructionSequence</span><span class="p">.</span><span class="nf">compile</span><span class="p">(</span><span class="s2">"puts 'hi'"</span><span class="p">,</span> <span class="s1">'hello_world.rb'</span><span class="p">).</span><span class="nf">eval</span>
<span class="no">Coverage</span><span class="p">.</span><span class="nf">result</span> <span class="c1"># => {}, should be {'hello_world.rb' => [1]}</span>
</code></pre>
<p>This is not intended, right?</p> Ruby master - Feature #15330 (Open): autoload_relativehttps://bugs.ruby-lang.org/issues/153302018-11-21T23:43:41Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>I'd like to propose a way to autoload a constant using a relative path.</p>
<p>It could look like:</p>
<pre><code>autoload_relative :MyConst, 'models/my_const'
</code></pre>
<p>My proposal raises two questions:</p>
<ol>
<li>what's the future of <code>autoload</code>?</li>
</ol>
<p>I believe that <code>autoload</code> has been there for years, it is used successfully and has no real alternative.</p>
<p>I looked at a sample of 430 top gems (took the 500 top ranked according to Libraries.io, removed those that I failed to process). The number of those gems that appear to use <code>autoload</code> at least once is 94 of those (22%).</p>
<p>The number of lines in the code where <code>autoload</code> is called can be quite big. The top 5 are:<br>
vagrant: 235<br>
yard: 206<br>
ffaker: 155<br>
aws-sdk: 152<br>
rdoc: 92</p>
<p>This is a minimum bound, as some gems might be using loops, my processing would only detect the one place in the code with <code>autoload</code>.</p>
<ol start="2">
<li>are many autoladed paths relative?</li>
</ol>
<p>My preliminary numbers indicate that of the 94 gems using autoload, at least 75 are autoloading some relative files. That's a lower bound, as my algorithm is pretty crude and will only count the simplest cases as being relative. An example of gem my algorithm does not detect is <code>yard</code>, because the author wrote a small method to map the relative paths to global paths (code here: <a href="https://github.com/lsegal/yard/blob/master/lib/yard/autoload.rb#L3" class="external">https://github.com/lsegal/yard/blob/master/lib/yard/autoload.rb#L3</a> )</p>
<p>Of those where my processing detects the relative requires, a vast majority are relative. The average is that 94% of autoloaded files are relative and would benefit from <code>require_relative</code></p>
<p>In summary: I am convinced that <code>autoload</code> should remain in Ruby indefinitely. <code>autoload_relative</code> would actually be more useful than <code>autoload</code>. Even if the future of <code>autoload</code> remains uncertain, I would recommend adding <code>autoload_relative</code>; if it is ever decided to actually remove <code>autoload</code>, removing <code>autoload_relative</code> would not really add to the (huge) burden of gem maintainers.</p> Ruby master - Feature #15277 (Open): at_exechttps://bugs.ruby-lang.org/issues/152772018-11-02T19:23:47Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>There's currently no easy way to have code executed before a subsequent call to <code>exec</code>. One has to monkey-patch the builtin method.</p>
<p>I'd like to propose a new method <code>at_exec</code> that would be very similar to <code>at_exit</code>, except that the callbacks are triggered before the current process is replaced by the external command.</p>
<pre><code># This would output "Hello", "Bye", and "Foo"
at_exec { puts "Bye!" }
puts "Hello"
exec "echo Foo"
</code></pre>
<p>Use case: we roll our own in <code>DeepCover</code>. Some test suites will call <code>exec</code>, and we need to store our counters before that happens.</p> Ruby master - Bug #15206 (Closed): require_relative in std_libhttps://bugs.ruby-lang.org/issues/152062018-10-06T03:17:08Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>I noticed that there are some <code>require</code> for internal files in <code>lib/</code>. Ideally, they would be using <code>require_relative</code> instead. This is faster and more explicit.</p>
<p>Note: <code>require_relative</code> had a potential issue with symlinks that was fixed in 2.5, so libraries that might be loaded from earlier Ruby, like <code>rubygems</code>, can not yet be updated.</p>
<p>I've <a href="https://github.com/ruby/ruby/pull/1976" class="external">created a PR</a> and would like to know if there are comments / objections / things I'm missing.</p> Ruby master - Bug #15078 (Closed): Hash splat of empty hash should not create a positional argument.https://bugs.ruby-lang.org/issues/150782018-09-05T16:22:45Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Looks like <a class="issue tracker-1 status-5 priority-4 priority-default closed" title="Bug: Splat with empty keyword args gives unexpected results (Closed)" href="https://bugs.ruby-lang.org/issues/10856">#10856</a> is not completely fixed, but I can't reopen it</p>
<pre><code>def foo(*args); args; end
foo(**{}) # => []
foo(**Hash.new) # => [{}], should be []
</code></pre> Ruby master - Bug #14674 (Closed): New mismatched indentations warnings?https://bugs.ruby-lang.org/issues/146742018-04-10T17:43:28Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>I recently got a failure in my test suite because ruby head warns of indentation it considers mismatched:</p>
<pre><code>$ ruby -w -e "
case
when :foo
end
"
-e:3: warning: mismatched indentations at 'when' with 'case' at 2
</code></pre>
<p>I hope this is not intentional and will be fixed.</p> Ruby master - Bug #14201 (Closed): Regression due to over optimization of hash splathttps://bugs.ruby-lang.org/issues/142012017-12-18T20:27:47Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>The following doesn't print anything nor raise anything in 2.5.0-rc1 or trunk:</p>
<pre><code>$ ruby -e "{**puts('hello')}; 42"
</code></pre>
<p>It should be the same as in Ruby 2.0-2.4:</p>
<pre><code>hello
-e:1:in `<main>': no implicit conversion of nil into Hash (TypeError)
</code></pre>
<p>Note: If you try to use the hash (e.g. passing as argument, storing in variable), then the correct behavior takes place. Found this bug through DeepCover's test suite.</p> Ruby master - Feature #14133 (Closed): Module#{define|alias|undef}_method should be made publichttps://bugs.ruby-lang.org/issues/141332017-11-28T03:26:16Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Modules and classes can be reopened and changed (unless frozen).</p>
<p>This is used in many meta programming techniques.</p>
<p>Currently, <code>define_method</code> is private, so we need to either do a <code>class_eval</code>, reopen the class somehow, or resort to <code>:send</code>.</p>
<p>As I previously stated in <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: public and private for core methods (Closed)" href="https://bugs.ruby-lang.org/issues/6539">#6539</a>, I feel that the use of <code>send</code> should be reserved for incorrect usage of actually private methods that might change of interface or aren't meant to be called this way (e.g. <code>respond_to_missing?</code>, <code>puts</code>)</p>
<p>Matz has stated before that "class/module operations should be done in the scope.". Nevertheless, common usage shows that there are many cases where Rubyists prefer using a single line for this, even if it means having to call send.</p>
<p>Here are 95k+ examples of <code>send :define_method</code> in the wild:<br>
<a href="https://github.com/search?utf8=%E2%9C%93&q=language%3Aruby+%22send+%3Adefine_method%22&type=Code" class="external">https://github.com/search?utf8=%E2%9C%93&q=language%3Aruby+%22send+%3Adefine_method%22&type=Code</a></p>
<p>Note that many of these are very bad usage of <code>define_method</code>. I feel this shows that even beginner programmers will use <code>:send</code> if privacy gets in the way. Let's not hinder advanced users in a failed attempt to protect the beginners.</p>
<p>Rails has good examples of use of <code>send :define_method</code> like <a href="https://github.com/rails/rails/blob/master/actionview/lib/action_view/lookup_context.rb#L27" class="external">https://github.com/rails/rails/blob/master/actionview/lib/action_view/lookup_context.rb#L27</a><br>
That's despite the fact for most metaprogramming they don't use <code>define_method</code> and instead build a string of code and do a <code>module_eval</code> for both performance and ease debugging (see the next lines in the example). That's not possible when the new methods must access blocks though, like in the example given.</p>
<p><code>alias_method</code> and <code>undef_method</code> can be seen as special cases of <code>define_method</code> and should have the same privacy.<br>
55k+ examples of <code>send :alias_method</code> in the wild:<br>
<a href="https://github.com/search?utf8=%E2%9C%93&q=language%3Aruby+%22send+%3Aalias_method%22&type=Code" class="external">https://github.com/search?utf8=%E2%9C%93&q=language%3Aruby+%22send+%3Aalias_method%22&type=Code</a><br>
30k+ examples of <code>send :undef_method</code> in the wild:<br>
<a href="https://github.com/search?utf8=%E2%9C%93&q=language%3Aruby+%22send+%3Aundef_method%22&type=Code" class="external">https://github.com/search?utf8=%E2%9C%93&q=language%3Aruby+%22send+%3Aundef_method%22&type=Code</a><br>
20k+ examples of <code>send :remove_method</code> in the wild:<br>
<a href="https://github.com/search?utf8=%E2%9C%93&q=language%3Aruby+%22send+%3Aundef_method%22&type=Code" class="external">https://github.com/search?utf8=%E2%9C%93&q=language%3Aruby+%22send+%3Aundef_method%22&type=Code</a></p> Ruby master - Feature #14132 (Closed): Module#attr{|_reader|_writer} should be publichttps://bugs.ruby-lang.org/issues/141322017-11-28T02:40:51Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Modules and classes can be reopened and changed (unless frozen).</p>
<p>This is used in many meta programming techniques.</p>
<p>Currently, <code>attr_accessor</code>, <code>attr_writer</code> and <code>attr_reader</code> are private, so we need to either do a <code>class_eval</code>, reopen the class somehow, or resort to <code>:send</code></p>
<p>As I previously stated in <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: public and private for core methods (Closed)" href="https://bugs.ruby-lang.org/issues/6539">#6539</a>, I feel that the use of <code>send</code> should be reserved for incorrect usage of actually private methods that might change of interface or aren't meant to be called this way (e.g. <code>respond_to_missing?</code>)</p>
<p>Matz has stated before that "class/module operations should be done in the scope.". Nevertheless, common usage shows that there are many cases where Rubyists prefer using a single line for this, even if it means having to call <code>send</code>.</p>
<p>Here are 15k+ examples of <code>send :attr_accessor</code> in the wild:<br>
<a href="https://github.com/search?utf8=%E2%9C%93&q=language%3Aruby+%22send+%3Aattr_accessor%22&type=Code" class="external">https://github.com/search?utf8=%E2%9C%93&q=language%3Aruby+%22send+%3Aattr_accessor%22&type=Code</a><br>
15k+ examples of <code>send :attr_writer</code> in the wild:<br>
<a href="https://github.com/search?utf8=%E2%9C%93&q=language%3Aruby+%22send+%3Aattr_writer%22&type=Code" class="external">https://github.com/search?utf8=%E2%9C%93&q=language%3Aruby+%22send+%3Aattr_writer%22&type=Code</a><br>
15k+ examples of <code>send :attr_reader</code> in the wild:<br>
<a href="https://github.com/search?utf8=%E2%9C%93&q=language%3Aruby+%22send+%3Aattr_reader%22&type=Code" class="external">https://github.com/search?utf8=%E2%9C%93&q=language%3Aruby+%22send+%3Aattr_reader%22&type=Code</a></p>
<p>Please make consider making <code>:attr</code>, <code>:attr_accessor</code>, <code>:attr_writer</code> and <code>:attr_reader</code> public.</p> Ruby master - Bug #14057 (Closed): TracePoint#enable and disable should not yield argumentshttps://bugs.ruby-lang.org/issues/140572017-10-26T06:54:53Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>While working on RubySpecs with Atul Bhosale, we discovered that <code>TracePoint#enable</code> and <code>#disable</code> yield <code>nil</code> instead of not yielding any argument.</p>
<p>This is mostly harmless as we usually use blocks, but it could create issues for lambdas/methods, for example:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">def</span> <span class="nf">handle_trace</span><span class="p">;</span> <span class="k">end</span>
<span class="no">TracePoint</span><span class="p">.</span><span class="nf">new</span><span class="p">{}.</span><span class="nf">enable</span><span class="p">(</span><span class="o">&</span><span class="nb">method</span><span class="p">(</span><span class="ss">:handle_trace</span><span class="p">))</span> <span class="c1"># => ArgumentError: wrong number of arguments (given 1, expected 0)</span>
</code></pre>
<p>I'm fixing in trunk, would be nice to backport.</p> Ruby master - Bug #14031 (Closed): WeakRef example misleading and wronghttps://bugs.ruby-lang.org/issues/140312017-10-19T02:43:08Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>I just noticed that the second part of the doc of <code>WeakRef</code> is misleading and later plainly wrong.</p>
<p>I'm talking about the example with <code>WeakHash</code> in<br>
<a href="https://ruby-doc.org/stdlib-2.4.0/libdoc/weakref/rdoc/WeakRef.html" class="external">https://ruby-doc.org/stdlib-2.4.0/libdoc/weakref/rdoc/WeakRef.html</a></p>
<p>The example shows</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="no">GC</span><span class="p">.</span><span class="nf">start</span>
<span class="n">c</span><span class="p">[</span><span class="s1">'foo'</span><span class="p">]</span> <span class="c1">#=> nil</span>
<span class="n">c</span><span class="p">[</span><span class="s1">'baz'</span><span class="p">]</span> <span class="c1">#=> nil</span>
<span class="n">c</span><span class="p">[</span><span class="s1">'qux'</span><span class="p">]</span> <span class="c1">#=> nil</span>
</code></pre>
<p>This is very <em>misleading</em>, since even before the GC that would be also the case, because <code>WeakHash</code> didn't redefine the lookup, and that <code>WeakHash.new('foo').eql?('foo')</code> is always <code>false</code>.</p>
<p>The doc goes on with:<br>
"You can see the local variable <code>omg</code> stayed, although its reference in our hash object was garbage collected, along with the rest of the keys and values."</p>
<p>That is <em>wrong</em>. The reference in our hash object was not garbage collected, since <code>omg</code> held on to it. This can be proven with <code>c.values.last # => 'lol'</code>.</p>
<p>My opinion is that fixing this example isn't worth it, and that even fixed it wouldn't add anything to the first simple example on WeakRef. In it's current form, it is worse dans not having it. Unless there are objections, I'll simply remove it.</p> Ruby master - Bug #14015 (Closed): Enumerable & Hash yielding arityhttps://bugs.ruby-lang.org/issues/140152017-10-14T20:19:18Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>The subtle difference between <code>yield 1, 2</code> and <code>yield [1, 2]</code> has always confused me.</p>
<p>Today I wanted to pass a method to Hash#flat_map and realized how it's even more confusing than I thought.</p>
<p>I assumed that <code>Hash#each</code> was calling <code>yield key, value</code>. But somehow it's not that simple:</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="nf">map</span><span class="p">(</span><span class="o">&-></span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">value</span><span class="p">){})</span> <span class="c1"># => [nil]</span>
<span class="p">{</span><span class="ss">a: </span><span class="mi">1</span><span class="p">}.</span><span class="nf">flat_map</span><span class="p">(</span><span class="o">&-></span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">value</span><span class="p">){})</span> <span class="c1">#=> ArgumentError: wrong number of arguments (given 1, expected 2)</span>
</code></pre>
<p>What blows my mind, is that a custom method <code>each</code> that does <code>yield a, 1</code> has different result!</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="o"><<</span> <span class="n">o</span> <span class="o">=</span> <span class="no">Object</span><span class="p">.</span><span class="nf">new</span>
<span class="kp">include</span> <span class="no">Enumerable</span>
<span class="k">def</span> <span class="nf">each</span>
<span class="k">yield</span> <span class="ss">:a</span><span class="p">,</span> <span class="mi">1</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">o</span><span class="p">.</span><span class="nf">map</span><span class="p">(</span><span class="o">&-></span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">value</span><span class="p">){})</span> <span class="c1"># => [nil]</span>
<span class="n">o</span><span class="p">.</span><span class="nf">flat_map</span><span class="p">(</span><span class="o">&-></span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">value</span><span class="p">){})</span> <span class="c1"># => [nil] does not raise!!</span>
</code></pre>
<p>I don't even know how that's possible, since Hash doesn't have a specialized <code>flat_map</code> method...</p>
<p>Here's a list of methods that accept a lambda of arity 2 (as I would expect)<br>
For Hash<br>
each, any?, map, select, reject,<br>
For a custom yield<br>
each, any?, map, count, find_index, flat_map, all?, one?, none?, take_while, uniq</p>
<p>These two lists have <code>each</code>, <code>map</code> and <code>any?</code> in common. Others work in one flavor, not the other. Many require arity 1: find, sort_by, grep, grep_v, count, detect, find_index, find_all, ...</p>
<p>To make things even more impossible, <code>Hash#map</code> has been working with arity 2 since Ruby 2.4 only.</p>
<p>Finally, <code>Hash#each</code> changes the expected arity of <code>select</code>, <code>reject</code>, and <code>any?</code>, but not of <code>map</code>:</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="p">.</span><span class="nf">select</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="c1"># => {}</span>
<span class="p">{</span><span class="ss">a: </span><span class="mi">1</span><span class="p">}.</span><span class="nf">each</span><span class="p">.</span><span class="nf">select</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="c1"># => wrong number of arguments (given 1, expected 2)</span>
</code></pre>
<p>Conclusion:</p>
<p>It seems more or less impossible to guess the expected arity of methods of Enumerable and of Hash, and they are not even consistent with one another. This makes these methods more or less unusable with lambdas.</p>
<p>While compatibility could be an issue, the fact that <code>Hash#map</code> has changed it's arity (I believe following <a href="https://bugs.ruby-lang.org/issues/13391" class="external">https://bugs.ruby-lang.org/issues/13391</a> ) makes me think that compatibility with the lesser used methods would be even less of a problem.</p>
<p>My personal wish: that the following methods be fixed to expect arity 2 for lambdas:</p>
<p>For both Hash & Enumerable:</p>
<ul>
<li>find, sort_by, grep, grep_v, detect, find_all, partition, group_by, min_by, max_by, minmax_by, reverse_each, drop_while, sum<br>
For Hash:</li>
<li>count, find_index, flat_map, all?, one?, none?, take_while, uniq<br>
For Enumerable:</li>
<li>select, reject</li>
</ul>
<p>Matz, what do you think?</p> Ruby master - Bug #14014 (Closed): NaN.finite?https://bugs.ruby-lang.org/issues/140142017-10-13T21:23:28Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Ruby gives contradictory answers for NaN:</p>
<pre><code>> (0/0.0).finite?
=> false
> Complex(0/0.0, 0).finite?
=> true
</code></pre>
<p>Note that <code>(0/0.0).infinite?</code> returns <code>nil</code>, so the float answer of <code>false</code> looks even more wrong.</p>
<p>The two solutions I see are either changing <code>Float#finite?</code> to return <code>true</code> for NaN, or to raise in both cases.</p>
<p>I'd lean towards raising in both cases, as NaN can not be said to be finite or infinite</p> Ruby master - Bug #13973 (Closed): super_method fails on some UnboundMethodshttps://bugs.ruby-lang.org/issues/139732017-10-05T02:01:33Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p><code>super_method</code> fails to go up the ancestry chain for methods that are only defined in included modules:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">module</span> <span class="nn">A</span>
<span class="k">def</span> <span class="nf">foo</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">module</span> <span class="nn">B</span>
<span class="k">def</span> <span class="nf">foo</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">class</span> <span class="nc">C</span>
<span class="kp">include</span> <span class="no">A</span>
<span class="kp">include</span> <span class="no">B</span>
<span class="k">end</span>
<span class="k">class</span> <span class="nc">D</span>
<span class="k">def</span> <span class="nf">foo</span>
<span class="k">end</span>
<span class="kp">include</span> <span class="no">A</span>
<span class="kp">include</span> <span class="no">B</span>
<span class="k">end</span>
<span class="no">C</span><span class="p">.</span><span class="nf">instance_method</span><span class="p">(</span><span class="ss">:foo</span><span class="p">)</span> <span class="c1"># => #<UnboundMethod: C(B)#foo> (ok)</span>
<span class="no">C</span><span class="p">.</span><span class="nf">instance_method</span><span class="p">(</span><span class="ss">:foo</span><span class="p">).</span><span class="nf">super_method</span> <span class="c1"># => nil (wrong, should be <UnboundMethod: <something>(A)#foo>)</span>
<span class="no">C</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">method</span><span class="p">(</span><span class="ss">:foo</span><span class="p">).</span><span class="nf">super_method</span> <span class="c1"># => #<Method: Object(A)#foo> (ok)</span>
<span class="no">D</span><span class="p">.</span><span class="nf">instance_method</span><span class="p">(</span><span class="ss">:foo</span><span class="p">).</span><span class="nf">super_method</span> <span class="c1"># => #<UnboundMethod: Object(B)#foo> (ok)</span>
<span class="no">D</span><span class="p">.</span><span class="nf">instance_method</span><span class="p">(</span><span class="ss">:foo</span><span class="p">).</span><span class="nf">super_method</span><span class="p">.</span><span class="nf">super_method</span> <span class="c1"># => #<UnboundMethod: Object(A)#foo> (ok)</span>
</code></pre>
<p>Note that the results for C and D's super_method differ slightly, with one outputing "C(B)" and the other "Object(B)". I don't understand why "Object" shows anywhere in my example. I would have expected the output to be "D(B)" in the later case. Should I open a different issue for this?</p> Ruby master - Feature #12813 (Closed): Calling chunk_while, slice_after, slice_before, slice_when...https://bugs.ruby-lang.org/issues/128132016-10-06T04:43:55Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Currently, <code>chunk_while</code>, <code>slice_after</code>, <code>slice_before</code>, <code>slice_when</code> all require a block.</p>
<p>If one needs the index within the block, there is no good way to do this; <code>enum.each_with_index.chunk_while</code> would have indices in the results, so <code>enum.enum_for(:chunk_while).with_index</code> is the best solution.</p>
<p>I feel that we should return <code>enum_for(:chunk_while)</code>. This is strictly more useful than raising as we currently do.</p> Ruby master - Feature #11816 (Assigned): Partial safe navigation operatorhttps://bugs.ruby-lang.org/issues/118162015-12-14T18:19:37Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>I'm extremely surprised (and disappointed) that, currently:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">x</span> <span class="o">=</span> <span class="kp">nil</span>
<span class="n">x</span><span class="o">&</span><span class="p">.</span><span class="nf">foo</span><span class="p">.</span><span class="nf">bar</span> <span class="c1"># => NoMethodError: undefined method `bar' for nil:NilClass</span>
</code></pre>
<p>To make it safe, you have to write <code>x&.foo&.bar</code>. But if <code>foo</code> is never supposed to return <code>nil</code>, then that code isn't "fail early" in case it actually does. <code>nil&.foo.bar</code> is more expressive, simpler and is perfect if you want to an error if <code>foo</code> returned <code>nil</code>. To actually get what you want, you have to resort using the old form <code>x && x.foo.bar</code>...</p>
<p>In CoffeeScript, you can write <code>x()?.foo.bar</code> and it will work well, since it gets compiled to</p>
<pre><code class="js syntaxhl" data-language="js"><span class="k">if </span><span class="p">((</span><span class="nx">_ref</span> <span class="o">=</span> <span class="nf">x</span><span class="p">())</span> <span class="o">!=</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">_ref</span><span class="p">.</span><span class="nx">foo</span><span class="p">.</span><span class="nx">bar</span><span class="p">;</span>
<span class="p">}</span>
</code></pre>
<p>All the discussion in <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Introduce "Safe navigation operator" (Closed)" href="https://bugs.ruby-lang.org/issues/11537">#11537</a> focuses on <code>x&.foo&.bar</code>, so I have to ask:</p>
<p>Matz, what is your understanding of <code>x&.foo.bar</code>?</p>
<p>I feel the current implementation is not useful and should be changed to what I had in mind. I can't see any legitimate use of <code>x&.foo.bar</code> currently.</p> Ruby master - Bug #11776 (Closed): dig and custom objectshttps://bugs.ruby-lang.org/issues/117762015-12-06T05:18:02Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Although currently undocumented and untested, it is possible to <code>dig</code> objects of any class that implements <code>dig</code>:</p>
<pre><code>class Foo
def dig(x, *)
40 + x
end
end
{hello: Foo.new}.dig(:hello, 2) # => 42
</code></pre>
<p>This seems actually quite nice to me.</p>
<p>Matz, could you confirm that this is part of the new feature? I'll fix the documentation and add some basic tests</p> Ruby master - Bug #10831 (Closed): Variable keyword arguments shouldn't create immortal symbolshttps://bugs.ruby-lang.org/issues/108312015-02-05T19:58:53Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Calling a method with keyword arguments will, sometimes, create immortal symbols.</p>
<p>The following tests should not fail:</p>
<pre><code>def test_kwarg_symbol_leak_no_rest
foo = -> (arg: 42) {}
assert_no_immortal_symbol_created("kwarg no rest") do |name|
assert_raise(ArgumentError) { foo.call(name.to_sym => 42) }
end
end
def test_kwarg_symbol_leak_with_rest
foo = -> (arg: 2, **options) {}
assert_no_immortal_symbol_created("kwarg with rest") do |name|
foo.call(name.to_sym => 42)
end
end
def test_kwarg_symbol_leak_just_rest
foo = -> (**options) {}
assert_no_immortal_symbol_created("kwarg just rest") do |name|
foo.call(name.to_sym => 42)
end
end
</code></pre>
<p>Note: the last one succeeds (because the hash is simply cloned internally), and is there for completeness.</p> Ruby master - Bug #10828 (Closed): send should not create immortal symbolshttps://bugs.ruby-lang.org/issues/108282015-02-04T19:01:50Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>While <code>public_send</code> is ok, <code>send</code> and <code>__send__</code> create immortal symbols when they shouldn't.</p> Ruby master - Feature #10354 (Closed): Optimize Integer#prime?https://bugs.ruby-lang.org/issues/103542014-10-10T03:22:13Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Nick Slocum shows in <a href="https://github.com/ruby/ruby/pull/736" class="external">https://github.com/ruby/ruby/pull/736</a> that Integer#prime? can be optimized quite a bit.</p>
<p>First, that's because there are some basic things to avoid in the current lib, like needlessly capturing blocks and there's a useless <code>loop do</code> too.</p>
<p>I'm attaching a patch that fixes many of these things.</p>
<p>Even after these fixes applied, Nick's version is still faster and I don't see why we would not use it for Fixnum#prime?</p>
<p>For Bignum#prime?, since division costs more, we can go slightly faster with the following implementation:</p>
<pre><code>class Integer
# Returns true if +self+ is a prime number, else returns false.
def prime?
return true if self == 2
return false if self % 2 == 0 || self % 3 == 0 || self < 2
skip_division = true
(5..(self**0.5).floor).step(2) do |i|
return false if skip_division && self % i == 0
skip_division = !skip_division
end
true
end
end
</code></pre> Ruby master - Bug #9660 (Closed): test/unit, minitest & bundlerhttps://bugs.ruby-lang.org/issues/96602014-03-21T19:15:10Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>test/unit now calls <code>gem 'minitest'</code>, but this will create regressions for anyone using <code>bundler</code>.</p>
<p>For example, create an empty <code>Gemfile</code> and try <code>bundle exec ruby -e "require 'test/unit'"</code></p>
<p>You get an error:</p>
<pre><code>.rvm/gems/ruby-head@global/gems/bundler-1.5.3/lib/bundler/rubygems_integration.rb:240:in `block in replace_gem': minitest is not part of the bundle. Add it to Gemfile. (Gem::LoadError)
</code></pre>
<p>See: <a href="https://github.com/ruby/ruby/commit/da61291a25faae95f33de6756b2eaa4804d5ef2b#commitcomment-5761129" class="external">https://github.com/ruby/ruby/commit/da61291a25faae95f33de6756b2eaa4804d5ef2b#commitcomment-5761129</a></p> Backport21 - Backport #9575 (Closed): Step with 0 step is buggyhttps://bugs.ruby-lang.org/issues/95752014-02-27T17:25:15Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>I didn't realize that we now allow stepping with a '0' step. It should probably have been mentioned in the NEWS of 2.1.0?</p>
<p>Anyways, couple of bugs with that new feature:</p>
<pre><code>bn = 1 << 100
bn.step(by: 0, to: bn).first(2) # => [bn, bn] ok
bn.step(by: 0).first(2) # => [bn.to_f, bn.to_f] not ok
bn.step(by: 0, to: 0).first(2) # => [] not ok
</code></pre>
<p>The corresponding <code>size</code> don't all work either:</p>
<pre><code>bn.step(by: 0) # => Float::INFINITY, ok
bn.step(by: 0, to: bn).size # => ZeroDivisionError: divided by 0, should be infinity
bn.step(by: 0, to: 0).size # => same
1.step(by:0, to: 42).size # => same
</code></pre>
<p>My patch is almost finished.</p> Ruby master - Feature #9347 (Open): Accept non callable argument to detecthttps://bugs.ruby-lang.org/issues/93472014-01-03T07:37:33Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Currently, the only argument that <code>Enumerable#detect</code> accepts is a callable object.</p>
<p>Shouldn't we accept non callable objects too?</p>
<pre><code>[42].detect(:not_found){} # => NoMethodError: undefined method `call' for :not_found:Symbol
# would return :not_found instead.
</code></pre>
<p>I'd suggest that if the given argument does not <code>respond_to? :call</code>, then it would be returned as is instead of raising an error as currently.<br>
Wouldn't this be more flexible and possibly more performant?</p>
<p>Inspired by <a href="http://stackoverflow.com/questions/20883414/why-does-enumerabledetect-need-a-proc-lambda" class="external">http://stackoverflow.com/questions/20883414/why-does-enumerabledetect-need-a-proc-lambda</a></p> Backport21 - Backport #9299 (Closed): Required keyowrd arguments and arityhttps://bugs.ruby-lang.org/issues/92992013-12-26T06:33:41Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>While fixing <a class="issue tracker-4 status-5 priority-4 priority-default closed" title="Backport: Method#arity for keyword arguments (Closed)" href="https://bugs.ruby-lang.org/issues/8072">#8072</a>, I noticed another bug: a required keyword argument should add 1 to the arity:</p>
<pre><code>proc{|required:|}.arity # => 0, should be 1
</code></pre> Backport21 - Backport #9270 (Closed): Array#to_h should not ignore badly formed elementshttps://bugs.ruby-lang.org/issues/92702013-12-21T02:59:27Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>The upcoming Array#to_h feature currently ignores elements that are not 2-elements arrays. Array#to_h could instead raise an error on those elements. I argued otherwise before, but maybe that would be safer.</p>
<p>One reason I think I was wrong is that current form could encourage code like:</p>
<p>enum.map{|x| [x.foo, x.bar] if x.baz? }.to_h</p>
<p>using the fact that any <code>nil</code> will be ignored. I'm not sure that it's a good idea.</p>
<p>It would probably be safer to raise an Exception for elements that are not a key-value pair. It also satisfies fail-early principle.</p>
<p><a class="user active user-mention" href="https://bugs.ruby-lang.org/users/2963">@sawa (Tsuyoshi Sawada)</a> agrees with this.<br>
<a class="user active user-mention" href="https://bugs.ruby-lang.org/users/13">@matz (Yukihiro Matsumoto)</a> agrees with the change.</p>
<p>Since this feature has not already been released in an official version, changing this behavior now would not cause any incompatibility and there should be no risk of regression. Changing this feature after the official 2.1 release would be more problematic as it could cause incompatibilities.</p>
<p>Yui, could you please confirm that there is no problem on your end for me to commit the following patch: <a href="https://github.com/marcandre/ruby/compare/to_h_raise" class="external">https://github.com/marcandre/ruby/compare/to_h_raise</a></p> Ruby master - Bug #9242 (Closed): Rdoc detection of aliaseshttps://bugs.ruby-lang.org/issues/92422013-12-12T04:46:38Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>The online doc appears to be making bad detection of aliases.</p>
<p>For example, Array#map and Array#collect are not marked as aliases (on either ruby-doc.org or docs.ruby-lang.org)</p>
<p>On the other hand, when aliases are detected, the generated method interface and the name of the method it is aliases to are always the same for both methods.</p>
<p>For example, the doc for Array#find_index says it is an alias to find_index and has interface showing index (again on both sites), e.g. <a href="http://docs.ruby-lang.org/en/2.0.0/Array.html#method-i-find_index" class="external">http://docs.ruby-lang.org/en/2.0.0/Array.html#method-i-find_index</a></p>
<p>It might also be a good idea to always have doc examples using both forms in case of aliases.</p> Ruby master - Bug #9048 (Closed): Remove legacy ±(binary) special cases.https://bugs.ruby-lang.org/issues/90482013-10-24T00:30:13Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Is there any reason not to get rid of the following special cases?</p>
<pre><code>'+(binary)'.to_sym # => :+ when expected :"+(binary)"
</code></pre>
<p>The following patch didn't reveal any failure in make test:</p>
<p>diff --git a/parse.y b/parse.y<br>
index 76fc9e7..6550235 100644<br>
--- a/parse.y<br>
+++ b/parse.y<br>
@@ -10045,8 +10045,6 @@ static const struct {<br>
} op_tbl[] = {<br>
{tDOT2, ".."},<br>
{tDOT3, "..."},</p>
<ul>
<li>{'+', "+(binary)"},</li>
<li>{'-', "-(binary)"},<br>
{tPOW, "<strong>"},<br>
{tDSTAR, "</strong>"},<br>
{tUPLUS, "+@"},<br>
diff --git a/test/ruby/test_m17n.rb b/test/ruby/test_m17n.rb<br>
index 7c37c8a..3ea346f 100644<br>
--- a/test/ruby/test_m17n.rb<br>
+++ b/test/ruby/test_m17n.rb<br>
@@ -1230,7 +1230,7 @@ class TestM17N < Test::Unit::TestCase</li>
</ul>
<p>def test_symbol_op<br>
ops = %w"</p>
<ul>
<li>
<pre><code> .. ... + - +(binary) -(binary) * / % ** +@ -@ | ^ & ! <=> > >= < <= ==
</code></pre>
</li>
</ul>
<ul>
<li>
<pre><code> .. ... + - * / % ** +@ -@ | ^ & ! <=> > >= < <= ==
=== != =~ !~ ~ ! [] []= << >> :: `
</code></pre>
"<br>
ops.each do |op|</li>
</ul> Ruby master - Bug #8894 (Closed): Fixnum#quo returns wrong result when given a floathttps://bugs.ruby-lang.org/issues/88942013-09-11T13:07:36Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Fixnum#quo is buggy.</p>
<p>2.quo(2.0) # => Rational(2, 2)</p>
<ol>
<li>Should return a float, not a rational</li>
<li>Moreover, that rational is invalid as it is not reduced.</li>
</ol>
<p>Noticed by David MacMahon <a href="/issues/8883">[ruby-core:57121]</a></p> Ruby master - Bug #8841 (Closed): Module#included_modules and prepended moduleshttps://bugs.ruby-lang.org/issues/88412013-08-31T08:17:39Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>The documentation for Module#included_modules currently states "Returns the list of modules included in +mod+."</p>
<p>This was never perfectly accurate, as the list also contains modules included in +mod+'s ancestors.</p>
<p>It now also includes prepended modules.</p>
<p>This is consistent with <code>include?</code> that returns true for prepended modules, but not quite consistent with <code>included</code> that does not get called for prepended modules.</p>
<p>Matz, could you confirm that current behavior is what you want?</p>
<p>If so, we should fix the documentation of <code>include?</code> and <code>included_modules</code>.</p> Ruby master - Feature #8840 (Rejected): Yielder#statehttps://bugs.ruby-lang.org/issues/88402013-08-31T07:52:02Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Defining an Enumerator that require a state is currently troublesome. For example, it is not really possible to define an equivalent of Lazy#drop in Ruby without making an assumption on the implementation.</p>
<p>To address this, I propose that we</p>
<p>(a) guarantee that a new Yielder object will be given for each enumeration<br>
(b) add a 'state' attribute to Yielder.</p>
<p>This way, one could implement Lazy#drop in a way similar to:</p>
<p>class Enumerator::Lazy < Enumerator<br>
def drop(n)<br>
n = n.to_i<br>
Lazy.new(self) do |yielder, *values|<br>
yielder.state ||= n<br>
if yielder.state > 0<br>
yielder.state -= 1<br>
else<br>
yielder.yield(*values)<br>
end<br>
end<br>
end<br>
end</p>
<p>Note that (a) is currently true for Ruby MRI, JRuby and Rubinius, but it is not explicit in the documentation.</p> Backport200 - Backport #8463 (Closed): Proc auto-splat bug with named argumentshttps://bugs.ruby-lang.org/issues/84632013-05-30T03:51:46Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
I'd expect a proc to either do an implicit splat or not, but right now it looks for options before doing the implicit splat. Should it not do it after doing the implicit splat?</p>
<p>I thought that when a proc had an argument list with more than one element, it was the same to call it with a single array argument than with the same array splatted:</p>
<p>Proc{|a, ...| ... }.call([...]) == Proc{|a, ...}| ... }.call(*[...]) # => Because of implicit splat</p>
<p>But we have currently:</p>
<p>Proc.new{|a, *b, **c| p a, b, c}.call(1,2, bar: 3)</p>
<a name="gt-1-2-bargt3-OK"></a>
<h1 >=> 1, [2], {:bar=>3} : OK<a href="#gt-1-2-bargt3-OK" class="wiki-anchor">¶</a></h1>
<p>Proc.new{|a, *b, **c| p a, b, c}.call([1,2, bar: 3])</p>
<a name="gt-1-2-bargt3-Expected-same-as-above"></a>
<h1 >=> 1, [2, {:bar=>3}], {}: Expected same as above<a href="#gt-1-2-bargt3-Expected-same-as-above" class="wiki-anchor">¶</a></h1>
<p>Proc.new{|(a, *b), **c| p a, b, c}.call([1,2], bar: 3)</p>
<a name="gt-1-2-bargt3-OK-2"></a>
<h1 >=> 1, [2], {:bar=>3} : OK<a href="#gt-1-2-bargt3-OK-2" class="wiki-anchor">¶</a></h1>
<p>Proc.new{|(a, *b), **c| p a, b, c}.call([[1,2], bar: 3])</p>
<a name="gt-1-2-bargt3-Expected-same-as-above-2"></a>
<h1 >=> [1, 2], [{:bar=>3}], {}: Expected same as above<a href="#gt-1-2-bargt3-Expected-same-as-above-2" class="wiki-anchor">¶</a></h1>
<p>As an additional note, this affects some methods of Enumerable when yielding multiple arguments.</p>
<p>For example:</p>
<pre><code>def each; yield 1, 2, bar: 3; end
include Enumerable
each{|a, *b, **c| p a, b, c} # => 1, [2], {:bar => 3}: ok
detect{|a, *b, **c| p a, b, c} # => 1, [2, {:bar => 3}], {}: should be the same, no?
</code></pre>
<p>=end</p> Backport200 - Backport #8236 (Closed): super & named parameters bughttps://bugs.ruby-lang.org/issues/82362013-04-08T13:45:36Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
class Base<br>
def foo(*args)<br>
p args<br>
end<br>
end</p>
<p>class A < Base<br>
def foo(arg, bar: 'x')<br>
super<br>
end<br>
end<br>
A.new.foo 42 # => [42, {:bar=>"x"}] (ok)</p>
<p>class B < Base<br>
def foo(*args, bar: 'x')<br>
super<br>
end<br>
end<br>
B.new.foo 42 # => [[42], [:bar, "x"]] (not ok, should be same)<br>
=end</p> Backport200 - Backport #8188 (Closed): Wrong warning about __attached__https://bugs.ruby-lang.org/issues/81882013-03-30T13:34:02Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>ruby -w -e "ARGF.singleton_class.instance_eval{}"</p>
<a name="gt-warning-instance-variable-attached-not-initialized"></a>
<h1 >=> warning: instance variable <strong>attached</strong> not initialized<a href="#gt-warning-instance-variable-attached-not-initialized" class="wiki-anchor">¶</a></h1>
<p>Here's the test, but I'm not sure how to fix. Should rb_ivar_get(..., id_attached) replaced with rb_attr_get?</p>
<pre><code>diff --git a/test/ruby/test_eval.rb b/test/ruby/test_eval.rb
index 580d3e8..8e3bd8c 100644
--- a/test/ruby/test_eval.rb
+++ b/test/ruby/test_eval.rb
@@ -215,6 +215,12 @@ class TestEval < Test::Unit::TestCase
end
end
+ def test_instance_eval_on_argf_singleton_class
+ assert_warning('', '[ruby-core:xxx]') do
+ ARGF.singleton_class.instance_eval{}
+ end
+ end
+
class Foo
Bar = 2
end
</code></pre> Ruby master - Bug #8162 (Closed): Documentation for trust/taint lackinghttps://bugs.ruby-lang.org/issues/81622013-03-25T11:04:42Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>If would be good to make the documentation for {un}{trust|taint}{ed?} clearer. c.f. <a href="https://blade.ruby-lang.org/ruby-core/53679">[ruby-core:53679]</a></p>