Ruby Issue Tracking System: Issueshttps://bugs.ruby-lang.org/https://bugs.ruby-lang.org/favicon.ico?17113305112021-04-28T04:49:30ZRuby Issue Tracking System
Redmine 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 #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 - 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 - 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 #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 - 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 #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 #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 - 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 - 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 #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 - 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 #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 #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 - 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> 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> Backport21 - Backport #8072 (Closed): Method#arity for keyword argumentshttps://bugs.ruby-lang.org/issues/80722013-03-11T10:18:49Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>I would expect the following two methods to have the same arity:</p>
<pre><code>def old_way(req, options = {}); end
def new_way(req, **options); end
method(:new_way).arity # => 1, should be -2
</code></pre> Ruby master - Bug #7765 (Closed): Proc#arity bug with optional argumenthttps://bugs.ruby-lang.org/issues/77652013-02-01T05:38:17Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>We have:</p>
<pre><code>Proc.new{|a, b, c, d, e|}.arity # => 5
</code></pre>
<p>Matz decided <a href="/issues/5694">[ruby-core:46515]</a> we also have:</p>
<pre><code>Proc.new{|a, b, c, d, e=0|}.arity # => 4
</code></pre>
<p>It looks like we currently have:</p>
<pre><code>Proc.new{|a, b=0, c, d, e|}.arity # => 1, should be same as above
</code></pre>
<p>Hopefully there won't be disagreement that this is a bug?</p>
<p>I'm asking in particular because there was a specific test committed by Yui to that effect. I hope this was just an oversight?</p>
<pre><code>assert_equal(0, proc{|x=0, y|}.arity) # Should be 1, not 0. test/ruby/test_proc.rb:67
</code></pre>
<p>My patch is ready and I will commit it unless there is objection.</p>
<p><a href="https://github.com/marcandre/ruby/compare/marcandre:trunk...marcandre:proc_curry" class="external">https://github.com/marcandre/ruby/compare/marcandre:trunk...marcandre:proc_curry</a></p> Ruby master - Bug #7728 (Closed): Range#bsearch on other Numerics?https://bugs.ruby-lang.org/issues/77282013-01-23T03:12:30Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Range#bsearch attempts to do something on generic Numeric classes.</p>
<p>I feel it is both useless and buggy:</p>
<pre><code>(Rational(-1,2)..Rational(9,4)).bsearch{|x| true} # => yields with 7/8 and 33/16
(Rational(-1,2)..Rational(9,4)).bsearch{|x| false} # => loops forever
(BigDecimal('0.5')..BigDecimal('2.25'))... # => same
</code></pre>
<p>I feel the current implementation (aside from the bugs) only makes sense on Integers.</p>
<p>Possible approaches:</p>
<ul>
<li>Rational</li>
</ul>
<ol>
<li>convert to float, or</li>
<li>bsearch accepts a "max iterations" parameter (which would be required for Rational), or</li>
<li>forbid altogether</li>
</ol>
<ul>
<li>BigDecimal</li>
</ul>
<ol>
<li>convert to float, or</li>
<li>look at the space of decimal numbers in the range without a higher precision than begin or end.</li>
</ol>
<p>Given the timeframe though, I recommend we raise a TypeError for both in 2.0.0 (or a NotImplemented if we decide what should be done).</p> Ruby master - Bug #7726 (Closed): bsearch should handle block result in a consistent wayhttps://bugs.ruby-lang.org/issues/77262013-01-22T18:52:35Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>The documentation states that the block of bsearch must<br>
return either true/false or a number.</p>
<p>If the block returns another object (other than nil), I feel that either</p>
<ol>
<li>It should be considered as 'truthy'</li>
<li>It should raise a TypeError</li>
</ol>
<p>Currently it does not raise an error and returns a result different than if <code>true</code> was passed:</p>
<p>(1..3).bsearch{ 'x' } == (1..3).bsearch{ true } # => false</p> Ruby master - Bug #7725 (Closed): bsearch should return enumerator when called without a blockhttps://bugs.ruby-lang.org/issues/77252013-01-22T18:48:44Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>bsearch should return enumerator when called without a block</p>
<p>This would allow, for example</p>
<p>values.bsearch.with_index{|val, index| val >= some_array[index]} # => a value</p>
<p>Otherwise, one must use zip/each_with_index and will get an array returned, not just the value seeked:</p>
<p>r = values.zip(some_array).bsearch{|val, other_val| val >= other_val}<br>
a_value = r.first</p> Ruby master - Bug #7724 (Closed): 6 bugs with Range#bsearch over floatshttps://bugs.ruby-lang.org/issues/77242013-01-22T18:47:35Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Take the following code, with from, to and search all Integer or all Float:</p>
<p>(from...to).bsearch{|f| f >= search}</p>
<p>I expected it to:<br>
0) never yield and return nil if range is empty, i.e. from >= to ("trivial test")</p>
<ol>
<li>never yield more than 65 times for floats<br>
or Math.log(to-from, 2)+1 for Integer ("log test")</li>
<li>return search if from <= search < to, nil otherwise ("coverage test")</li>
<li>never yield the same <code>f</code> ("uniqueness test")</li>
<li>if range is exclusive, never yield <code>to</code> nor return <code>to</code>; if range is inclusive and block returns false always yield <code>to</code> ("end of range test")</li>
<li>never yield a number < from or > to ("out of range test")</li>
</ol>
<p>These conditions are all respected for Integers but all can be broken for Floats<br>
Test 0, 1, 3, 4 & 5 fail even for small ordinary float values, while test 2 requires some larger floats, say 1e100 to fail.<br>
For example bsearch can yield over 2000 times (instead of at most 65).</p>
<p>These tests and a correct Ruby implementation of bsearch can be found here: <a href="https://github.com/marcandre/backports/compare/marcandre:master...marcandre:bsearch" class="external">https://github.com/marcandre/backports/compare/marcandre:master...marcandre:bsearch</a><br>
My implementation also passes the MRI tests.</p> Ruby master - Bug #7715 (Closed): Lazy enumerators should want to stay lazy.https://bugs.ruby-lang.org/issues/77152013-01-19T02:33:26Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>I'm just waking up to the fact that many methods turn a lazy enumerator in a non-lazy one.</p>
<p>Here's an example from Benoit Daloze in <a href="/issues/6261">[ruby-core:44151]</a>:</p>
<p>lines = File.foreach('a_very_large_file').lazy<br>
.select {|line| line.length < 10 }<br>
.map {|line| line.chomp!; line }<br>
.each_slice(3)<br>
.map {|lines| lines.join(';').downcase }<br>
.take_while {|line| line.length > 20 }</p>
<p>That code will produce the right result but <em>will read the whole file</em>, which is not what is desired</p>
<p>Indeed, <code>each_slice</code> currently does not return a lazy enumerator :-(</p>
<p>To make the above code as intended, one must call <code>.lazy</code> right after the <code>each_slice(3)</code>. I feel this is dangerous and counter intuitive.</p>
<p>Is there a valid reason for this behavior? Otherwise, I would like us to consider returning a lazy enumerator for the following methods:<br>
(when called without a block)<br>
each_with_object<br>
each_with_index<br>
each_slice<br>
each_entry<br>
each_cons<br>
(always)<br>
chunk<br>
slice_before</p>
<p>The arguments are:</p>
<ul>
<li>fail early (much easier to realize one needs to call a final <code>force</code>, <code>to_a</code> or <code>each</code> than realizing that a lazy enumerator chain isn't actually lazy)</li>
<li>easier to remember (every method normally returning an enumerator returns a lazy enumerator). basically this makes Lazy covariant</li>
<li>I'd expect that if you get lazy at some point, you typically want to remain lazy until the very end</li>
</ul> Ruby master - Bug #7706 (Closed): Lazy#zip should not call `lazy`https://bugs.ruby-lang.org/issues/77062013-01-17T03:27:36Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Current implementation of Lazy#zip has problems:</p>
<ol>
<li>
<p>calls <code>lazy</code> when this is not excepted, necessary nor documented. Any reason to not call <code>each</code> instead?</p>
</li>
<li>
<p>calls <code>lazy</code> without checking previously for type:</p>
<p>[1].lazy.zip(42) # =>NoMethodError: undefined method `lazy' for 42:Fixnum</p>
<a name="expected-same-as-1zip42-ie-a-TypeError"></a>
<h1 >expected same as [1].zip(42), i.e. a TypeError<a href="#expected-same-as-1zip42-ie-a-TypeError" class="wiki-anchor">¶</a></h1>
</li>
<li>
<p>inefficient in the case where all arguments are arrays</p>
</li>
</ol>
<p>I'll address these when I get a chance. I don't understand why <code>lazy</code> is called instead of <code>each</code> though. Anyone?</p> Ruby master - Bug #7692 (Closed): Enumerator::Lazy#drop_while and take_while should require a block.https://bugs.ruby-lang.org/issues/76922013-01-14T16:33:07Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Enumerator::Lazy#drop_while and take_while should require a block.</p>
<p>Currently:</p>
<pre><code>[1].lazy.drop_while.force # => LocalJumpError: no block given
[1].lazy.take_while.force # => LocalJumpError: no block given
</code></pre>
<p>After patch, these will raise an ArgumentError "tried to call lazy drop_while without a block"</p> Ruby master - Feature #7292 (Closed): Enumerable#to_hhttps://bugs.ruby-lang.org/issues/72922012-11-07T02:53:45Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Now that #to_h is the official method for explicit conversion to Hash, we should also add</p>
<pre><code>Enumerable#to_h: Returns a hash for the yielded key-value pairs.
[[:name, 'Joe Smith'], [:age, 42]].to_h # => {name: 'Joe Smith', age: 42}
</code></pre>
<p>With the Ruby tradition of succint documentation I suggest the documentation talk about key-value pairs and there is no need to be explicit about the uninteresting cases like:</p>
<pre><code>(1..3).to_h # => {1 => nil, 2 => nil, 3 => nil}
[[1, 2], [1, 3]].to_h # => {1 => 3}
[[1, 2], []].to_h # => {1 => 2, nil => nil}
</code></pre>
<p>I see some reactions of people reading about the upcoming 2.0 release like this one:<br>
<a href="http://globaldev.co.uk/2012/11/ruby-2-0-0-preview-features/#dsq-comment-body-700242476" class="external">http://globaldev.co.uk/2012/11/ruby-2-0-0-preview-features/#dsq-comment-body-700242476</a></p> Ruby master - Bug #7248 (Closed): Shouldn't Enumerator::Lazy.new be private?https://bugs.ruby-lang.org/issues/72482012-10-31T13:23:13Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Is there a reason why Enumerator::Lazy.new is not private?</p>
<p>Lazy enumerators should be created with <code>Enumerable#lazy</code>. Moreover, there is no doc, and it can give unexpected results too.</p> Ruby master - Feature #6636 (Closed): Enumerable#sizehttps://bugs.ruby-lang.org/issues/66362012-06-24T02:49:38Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Now that it has been made clear that <code>Enumerable#count</code> never calls <code>#size</code> and that we have <code>Enumerable#lazy</code>, let me propose again an API for a lazy way to get the size of an Enumerable: <code>Enumerable#size</code>.</p>
<ul>
<li>call-seq:</li>
<li>enum.size # => nil, Integer or Float::INFINITY</li>
<li>
<li>Returns the number of elements that will be yielded, without going through</li>
<li>the iteration (i.e. lazy), or +nil+ if it can't be calculated lazily.</li>
<li>
<li>perm = (1..100).to_a.permutation(4)</li>
<li>perm.size # => 94109400</li>
<li>perm.each_cons(2).size # => 94109399</li>
<li>loop.size # => Float::INFINITY</li>
<li>[42].drop_while.size # => nil</li>
</ul>
<p>About 66 core methods returning enumerators would have a lazy <code>size</code>, like <code>each_slice</code>, <code>permutation</code> or <code>lazy.take</code>.</p>
<p>A few would have <code>size</code> return <code>nil</code>:<br>
Array#{r}index, {take|drop}_while<br>
Enumerable#find{_index}, {take|drop}_while<br>
IO: all methods</p>
<p>Sized enumerators can also be created naturally by providing a block to <code>to_enum</code>/<code>enum_for</code> or a lambda to <code>Enumerator.new</code>.</p>
<p>Example for <code>to_enum</code>:</p>
<pre><code>class Integer
def composition
return to_enum(:composition){ 1 << (self - 1) } unless block_given?
yield [] if zero?
downto(1) do |i|
(self - i).composition do |comp|
yield [i, *comp]
end
end
end
end
4.composition.to_a
# => [[4], [3, 1], [2, 2], [2, 1, 1], [1, 3], [1, 2, 1], [1, 1, 2], [1, 1, 1, 1]]
42.composition.size # => 2199023255552
</code></pre>
<p>Example for <code>Enumerator.new</code>:</p>
<pre><code>def lazy_product(*enums)
sizer = ->{
enums.inject(1) do |product, e|
break if (size = e.size).nil?
product * size
end
}
Enumerator.new(sizer) do |yielder|
# ... generate combinations
end
end
lazy_product(1..4, (1..3).each_cons(2)).size # => 8
lazy_product(1..4, (1..3).cycle).size # => Float::INFINITY
</code></pre> Ruby master - Feature #6589 (Closed): Set#rehashhttps://bugs.ruby-lang.org/issues/65892012-06-14T11:54:40Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>There should be a way to rehash a <code>Set</code>.</p>
<pre><code>s = Set.new([[]])
s.first << 1
# s.rehash # Does not exist!
s.include? [1] # => false, want true
</code></pre>
<p>See also:</p>
<p><a href="http://stackoverflow.com/questions/10992423/is-this-expected-behaviour-for-a-set-of-arrays-in-ruby" class="external">http://stackoverflow.com/questions/10992423/is-this-expected-behaviour-for-a-set-of-arrays-in-ruby</a><br>
<a href="http://stackoverflow.com/questions/10361400/deleting-a-modified-object-from-a-set-in-a-no-op" class="external">http://stackoverflow.com/questions/10361400/deleting-a-modified-object-from-a-set-in-a-no-op</a></p> Ruby master - Feature #6588 (Closed): Set#intersect?https://bugs.ruby-lang.org/issues/65882012-06-14T11:47:20Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>There is <code>Set#superset?</code>, <code>Set#subset?</code> with their <code>proper</code> variants, but there is no <code>Set#intersect?</code> nor <code>Set#disjoint?</code></p>
<p>To check if two sets intersect, one can do</p>
<p>set.intersection(other).empty?</p>
<p>This cycles through all elements, though. To be efficient, one can instead do the iteration manually:</p>
<p>other.any? { |x| set.include?(x) }</p>
<p>I think it would be natural to add <code>Set#intersect?</code> and its reverse <code>Set#disjoint?</code></p>
<p>class Set<br>
def intersect?(enum)<br>
enum.any? { |x| include?(x) }<br>
end<br>
end</p>
<p>Maybe it would be worth it to optimize it if enum is a larger Set by starting it with</p>
<pre><code> return any? { |x| enum.include?(x) } if enum.is_a?(Set) && enum.size > size
</code></pre> Ruby master - Feature #6276 (Closed): to_h as explicit conversion to Hashhttps://bugs.ruby-lang.org/issues/62762012-04-11T00:06:43Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Dear Matz.</p>
<p>We should establish #to_h as the method for explicit conversion to Hash:</p>
<ol>
<li>Add Hash#to_h: returns <code>self</code> for Hash, or a Hash for subclasses of Hash</li>
<li>Add Struct#to_h: as requested in Feature <a class="issue tracker-2 status-6 priority-4 priority-default closed" title="Feature: Struct#to_hash (Rejected)" href="https://bugs.ruby-lang.org/issues/4862">#4862</a> under the wrong name</li>
<li>Add OpenStruct#to_h: see Feature <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Please add a method to enumerate fields in OpenStruct (Closed)" href="https://bugs.ruby-lang.org/issues/1400">#1400</a></li>
</ol>
<p>In addition, please consider adding NilClass#to_h.</p>
<p>Even though you are not completely satisfied with NilClass#to_a, I use it very often. If we ever have something equivalent to the splat operator for hashes, the same hesitations will apply to <code>nil.to_h</code>, but I feel it would still bring more good than problems.</p>
<p>Thanks</p> Ruby master - Bug #6120 (Closed): Float and BigDecimal bug in remainder in corner caseshttps://bugs.ruby-lang.org/issues/61202012-03-07T14:51:41Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Currently:</p>
<pre><code>4.2.remainder(+Float::INFINITY) # => 4.2, ok
4.2.remainder(-Float::INFINITY) # => NaN, should be 4.2
# (same with all signs reversed)
</code></pre>
<p>Reasons the remainder should be 4.2 and not NaN:</p>
<ol>
<li>foo.remainder(bar) == foo.remainder(-bar)</li>
<li>foo.remainder(bar) == foo when bar.abs > foo.abs</li>
</ol>
<p>Similarly:<br>
require 'bigdecimal'<br>
bd = BigDecimal.new("4.2")<br>
bd.remainder(BigDecimal.new("+Infinity")) # => NaN, should be bd<br>
bd.remainder(BigDecimal.new("-Infinity")) # => NaN, should be bd<br>
# (same with all signs reverse)</p>
<p>Reasons: same as float.</p>
<p>Finally:<br>
bd = BigDecimal.new("4.2")<br>
bd.modulo(BigDecimal.new("0")) # => ZeroDivisionError, probably ok?<br>
bd.remainder(BigDecimal.new("0")) # => NaN, should be probably raise a ZeroDivisionError?</p>
<p>Like in <a class="issue tracker-4 status-5 priority-4 priority-default closed" title="Backport: Float#% bug in cornercases (Closed)" href="https://bugs.ruby-lang.org/issues/6044">#6044</a>, this could be decided either way, as long as there is consistency. Anyone prefer NaN to raising a ZeroDivisionError?</p> Ruby master - Bug #6085 (Closed): Treatment of Wrong Number of Argumentshttps://bugs.ruby-lang.org/issues/60852012-02-25T15:47:19Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>For brevity, let me abbreviate:</p>
<pre><code>WNA = "wrong number of arguments"
</code></pre>
<p>Ruby could provide more accurate information when raising an ArgumentError for WNA.</p>
<p>Example:</p>
<p>def foo(a, b=42); end<br>
foo # => WNA (0 for 1)<br>
for(1,2,3) # => WNA (3 for 2)</p>
<p>It would be strictly superior if the message said instead "WNA (0 for 1..2)" and "WNA (3 for 1..2)":</p>
<ul>
<li>
<p>more useful as it gives more information at a glance</p>
</li>
<li>
<p>consistent with calling builtin methods:</p>
<p>"".index # => WNA (0 for 1..2)<br>
"".index(1,2,3) # => WNA (3 for 1..2)</p>
</li>
</ul>
<p>Ruby is also not always consistent in its wording when there is a *rest argument:</p>
<p>Enumerator.new # => WNA (0 for 1+)<br>
[].insert # => WNA (at least 1)</p>
<p>File.chown # => WNA (0 for 2+)<br>
Process.kill # => WNA (0 for at least 2)</p>
<p>While reviewing and factorizing all WNA errors, I also found a problematic case:</p>
<p>"".sub(//) # => WNA (1 for 1..2)</p>
<p>It would probably less confusing if it said (1 for 2), as the form without a block requires 2 parameters. Same applies to <code>sub!</code></p>
<p>Also, <code>Module#define_method</code> could say "WNA (3 for 1)" when it actually accepts only up to 2 arguments.</p>
<p>I've implemented two patches that address all these issues.</p>
<p>The first one improves the error message when calling user methods and lambdas.</p>
<p>The second harmonizes the builtin methods and fixes the few that need to be fixed.</p>
<p>The two commits can be found here:</p>
<p><a href="https://github.com/marcandre/ruby/commits/rb_arity_check" class="external">https://github.com/marcandre/ruby/commits/rb_arity_check</a></p>
<p>Complete list of changes:</p>
<ul>
<li>
<p>Improvements:</p>
<p>"".sub(//): WNA (1 for 1..2) => WNA (1 for 2)<br>
(same with sub)<br>
Module#define_method: WNA (3 for 1) => WNA (3 for 1..2)<br>
exec: WNA => WNA (0 for 1+)<br>
Hash.new(1){}: WNA => WNA (1 for 0)<br>
instance_eval("", "", 1, 2)<br>
WNA instance_eval(...) or instance_eval{...}<br>
=> WNA (4 for 1..3)<br>
(same with module_eval and class_eval)<br>
Module#mix: WNA (4 for 1) => WNA (4 for 1..3)<br>
Module#mix, with incorrect arguments: WNA (2 for 1) => wrong arguments</p>
</li>
</ul>
<p>Wording change:</p>
<ul>
<li>
<p>Change of language: WNA (at least 1) => WNA (0 for 1+)<br>
[].insert<br>
extend<br>
"".delete!<br>
"".count</p>
</li>
<li>
<p>Process.kill: WNA (0 for at least 2) => WNA (0 for 2+)</p>
</li>
</ul>
<p>Also, builtin functions calling <code>rb_scan_args</code> with both optional arguments and a rest argument would generate an error of the form "WNA (0 for 2..3+)". After this patch, this would now read "WNA (0 for 2+)", again for consistency. The only two such cases I found are in <code>ext/win32ole.c</code></p>
<p>In addition to giving a more consistent error handling, these commits pave the way to:</p>
<ul>
<li>improved error reporting for parameters with named parameters (forthcoming issue)</li>
<li>improved checking for Proc#curry (see bug <a class="issue tracker-1 status-5 priority-4 priority-default closed" title="Bug: Proc#curry doesn't always detect too many arguments (Closed)" href="https://bugs.ruby-lang.org/issues/5747">#5747</a>)</li>
</ul> Ruby master - Bug #6048 (Closed): {Unbound}Method#hash doesn't always return the right valuehttps://bugs.ruby-lang.org/issues/60482012-02-20T15:19:08Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>{Unbound}Method#hash doesn't always return the right value.</p>
<pre><code>map, collect = Array.instance_method(:map), Array.instance_method(:collect)
map.eql?(collect) # => true
map.hash == collect.hash # => false
</code></pre>
<p>I'm tempted to think that this is an obvious bug with an obvious solution but let me state:</p>
<p>As per the documentation and the design of hash tables, if two objects are <code>eql?</code> then they must have the same hash.</p>
<p>Either <code>map</code> should not be <code>eql?</code> to <code>collect</code> or else their <code>hash</code> should be the same.</p>
<p>As they are identical methods, it is correct that they are <code>eql?</code>, so <code>hash</code> must return the same value.</p>
<p>My proposed behavior passes my strict superiority test and is also "straightforward" as I could find no intent for the current behavior.</p>
<p>One solution is to ensure that all aliased methods are defined using <code>rb_define_alias</code>, which appears to yield the same hash. Another is to compute the <code>hash</code> correctly. Maybe there is a third approach.</p>
<p>I'm not super confident I took the right approach, but here is a patch for the second one. I think it is more robust and more consistent with how <code>{Unbound}Method.eql?</code> is implemented. It may also fix other cases of mismatch between <code>eql?</code> and <code>hash</code>, I didn't investigate further.</p>
<p>I'd be grateful if someone could review the patch (Koichi?) and let me know if I took the right approach and if I put things in the right place.</p>
<a name="Thanks"></a>
<h2 >Thanks<a href="#Thanks" class="wiki-anchor">¶</a></h2>
<p>Marc-André</p> Ruby master - Bug #6029 (Closed): Should OpenStruct implement #eql? and #hash?https://bugs.ruby-lang.org/issues/60292012-02-15T12:33:15Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Currently, OpenStruct have no specialized #eql? and #hash, so:</p>
<pre><code>require 'ostruct'
x = OpenStruct.new(foo: 42)
y = x.dup
x == y # => true
x.eql?(y) # => false
</code></pre>
<p>This means that OpenStruct does not behave like Struct, Array, Hash and other structures by comparing its fields and values.</p>
<p>This also prevents using OpenStructs as hash keys (unless one uses the exact same object as the key), contrary to other structures.</p>
<p>Is there an historical reason for this?</p>
<p>How likely would it be that adding #eql? and #hash create conflict with some fields?</p> Ruby master - Bug #6028 (Closed): OpenStruct.dup doesn't have all its methodshttps://bugs.ruby-lang.org/issues/60282012-02-15T11:44:57Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Dupped OpenStructs don't have the same methods.</p>
<pre><code>x = OpenStuct.new(foo: 42)
x.dup.methods == x.methods # => false
x.respond_to?(:foo) # => true
x.dup.respond_to?(:foo) # => false
</code></pre> Ruby master - Bug #5747 (Closed): Proc#curry doesn't always detect too many arguments https://bugs.ruby-lang.org/issues/57472011-12-12T09:30:21Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Currently, Proc#curry checks the validity of the <code>arity</code> argument and will raise an error if:</p>
<ol>
<li>arity is less than the number of required arguments or</li>
<li>arity is more than the maximum number of arguments<br>
Note that simple Procs always accept any number of arguments (even though they may be ignored), so (2) doesn't applies to them, only to lambda's and procs made from methods.</li>
</ol>
<p>#2 isn't done as well as it could in case of limited optional parameters:</p>
<pre><code>def Object.foo(a, b=42); end
def Object.bar(a, b); end
Object.method(:foo).to_proc.curry(3) # => curried proc which will raise an error only when passed it's 3rd parameter
Object.method(:bar).to_proc.curry(3) # => ArgumentError: wrong number of arguments (3 for 2)
Same thing with lambdas
</code></pre>
<p>My proposed fix passes SST:<br>
a) usefulness: "fail-early" principle <a href="https://blade.ruby-lang.org/ruby-core/24130">[ruby-core:24130]</a><br>
b) consistency: both methods can only accept a maximum of 2 parameters and are thus treated consistently when attempting to curry with more than 2 arguments<br>
c) intuitiveness and d) performance: similar</p>
<p>It is straightforward (but not obvious), as it passes NIT (but not ODT).</p> Ruby master - Bug #5746 (Closed): Proc#curry too strict about lambda's arity.https://bugs.ruby-lang.org/issues/57462011-12-12T02:08:08Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Currently, Proc#curry raises an error when you attempt to curry a lambda with optional (but limited) arguments:</p>
<pre><code>l = ->(arg = 42) {}
l.curry(1) # => ArgumentError: wrong number of arguments (1 for 0)
</code></pre>
<p>Note that Proc#curry behaves correctly if the Proc was created from a method, even though arguments are treated the same way:</p>
<pre><code>def Object.foo(arg = 42); end
m = Object.method(:foo).to_proc
m.class # => Proc, same as 'l'
m.lambda? # => true, same as 'l'
m.curry(1) # => curried proc
</code></pre>
<p>Referring to my evaluation method, my proposed fix passes SST:<br>
a) usefulness: way more useful, as it makes it possible to do something that is not currently possible<br>
b) consistency: makes it consistent with equivalent method<br>
c) intuitiveness and d) performance: similar</p>
<p>It is straightforward (but not obvious), as it passes NIT (but not ODT).</p>
<p>This bug is a consequence of the redmine issue 5694; this will be fixed automatically when 5694 is fixed.</p> Ruby master - Bug #5307 (Closed): Matrix & subclasseshttps://bugs.ruby-lang.org/issues/53072011-09-11T05:22:18Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Matrix doesn't deal properly with subclasses:</p>
<pre><code>Class.new(Matrix)[[42]].class # => Matrix
</code></pre> Ruby master - Bug #5273 (Closed): Float#round returns the wrong floats for higher precisionhttps://bugs.ruby-lang.org/issues/52732011-09-05T05:43:55Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Float#round returns the wrong floats for higher precision.</p>
<p>Rounding can be off:<br>
2.5e-22.round(22) # => 2.0e-22</p>
<p>It can also return values that have extra decimals:<br>
2.5e-31.round(31) # => 3.0000000000000003e-31<br>
2.5e-36.round(36) # => 2.9999999999999998e-36</p>
<p>Both errors can occur at the same time too:<br>
2.5e-39.round(39) # => 2.0000000000000002e-39</p>
<p>I believe these errors occur only for ndigits >= 22. For ndigits > 22, 10**(-ndigits) is not an exact Float (as 5 ** 23 won't fit in the 53 bits of a double's mantissa). For ndigits < 22, there should be enough bits left to avoid rounding errors.</p>
<p>For ndigits >= 22, the algorithm to use should be similar to the one used to convert doubles to string.</p>
<p>Hopefully, this is the last bug for #round with a given precision.</p> Ruby master - Bug #5228 (Closed): Integer#round fails on some big negative numbershttps://bugs.ruby-lang.org/issues/52282011-08-25T07:50:29Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Integer#round fails for some big negative numbers:</p>
<p>(+25 * 10<strong>70).round(-71) # => 30...00<br>
(-25 * 10</strong>70).round(-71) # => -20...00, should be -30...00</p> Ruby master - Bug #5227 (Closed): Float#round fails on corner caseshttps://bugs.ruby-lang.org/issues/52272011-08-25T06:03:50Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Float#round fails on some corner cases:</p>
<p>42.0.round(300) # => 42.0<br>
42.0.round(308) # => Infinity, should be 42.0<br>
42.0.round(309) # => 42.0</p>
<p>1.0e307.round(1) # => 1.0e307<br>
1.0e307.round(2) # => Infinity, should be 1.0e307</p>
<p>These occur when the exponent of the intermediate value overflows.</p>
<p>The original code already had criteria for extreme values, but we can find much tighter ones, as explained in the patch below. This fixes the bugs above and optimizes for most trivial cases.</p>
<p>I'd be grateful if someone could look it over before I commit it, thanks.</p>
<p>diff --git a/numeric.c b/numeric.c<br>
index 272bbd1..22608c9 100644<br>
--- a/numeric.c<br>
+++ b/numeric.c<br>
@@ -1491,18 +1491,37 @@ flo_round(int argc, VALUE *argv, VALUE num)<br>
VALUE nd;<br>
double number, f;<br>
int ndigits = 0;</p>
<ul>
<li>
<p>int binexp;<br>
long val;</p>
<p>if (argc > 0 && rb_scan_args(argc, argv, "01", &nd) == 1) {<br>
ndigits = NUM2INT(nd);<br>
}<br>
number = RFLOAT_VALUE(num);</p>
</li>
</ul>
<ul>
<li>f = pow(10, abs(ndigits));</li>
<li>
<li>if (isinf(f)) {</li>
<li>
<pre><code> if (ndigits < 0) number = 0;
</code></pre>
</li>
<li>}</li>
<li>else {</li>
</ul>
<ul>
<li>frexp (number , &binexp);</li>
<li>
</ul>
<p>+/* Let <code>exp</code> be such that <code>number</code> is written as: "0.#{digits}e#{exp}",</p>
<ul>
<li>i.e. such that 10 ** (exp - 1) <= |number| < 10 ** exp</li>
<li>Recall that up to 17 digits can be needed to represent a double,</li>
<li>so if ndigits + exp >= 17, the intermediate value (number * 10 ** ndigits)</li>
<li>will be an integer and thus the result is the original number.</li>
<li>If ndigits + exp <= 0, the result is 0 or "1e#{exp}", so</li>
<li>if ndigits + exp < 0, the result is 0.</li>
<li>We have:</li>
<li>
<pre><code> 2 ** (binexp-1) <= |number| < 2 ** binexp
</code></pre>
</li>
<li>
<pre><code> 10 ** ((binexp-1)/log_2(10)) <= |number| < 10 ** (binexp/log_2(10))
</code></pre>
</li>
<li>
<pre><code> If binexp >= 0, and since log_2(10) = 3.322259:
</code></pre>
</li>
<li>
<pre><code> 10 ** (binexp/4 - 1) < |number| < 10 ** (binexp/3)
</code></pre>
</li>
<li>
<pre><code> binexp/4 <= exp <= binexp/3
</code></pre>
</li>
<li>
<pre><code> If binexp <= 0, swap the /4 and the /3
</code></pre>
</li>
<li>
<pre><code> So if ndigits + binexp/(3 or 4) >= 17, the result is number
</code></pre>
</li>
<li>
<pre><code> If ndigits + binexp/(4 or 3) < 0 the result is 0
</code></pre>
</li>
</ul>
<p>+*/</p>
<ul>
<li>if ((long)ndigits * (4 - (binexp < 0)) + binexp < 0) {</li>
<li>
<pre><code> number = 0;
</code></pre>
</li>
<li>}</li>
<li>else if ((long)(ndigits - 17) * (3 + (binexp < 0)) + binexp < 0) {</li>
<li>
<pre><code> f = pow(10, abs(ndigits));
if (ndigits < 0) {
double absnum = fabs(number);
if (absnum < f) return INT2FIX(0);
</code></pre>
</li>
</ul> Ruby master - Bug #5070 (Closed): CSV.generate should not modify the given option hashhttps://bugs.ruby-lang.org/issues/50702011-07-22T04:23:02Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>Currently:</p>
<pre><code>CSV.generate( {}.freeze ) # => RuntimeError: can't modify frozen Hash
</code></pre>
<p>I'm not sure where these tests would ideally go; In the attached patch, I've put them at the end of csv/test_interface</p>
<p>If you can, please make the modifications you see fit, or else I'll commit this in a of week or two.</p>
<p>(From <a href="http://stackoverflow.com/questions/6759487/ruby-hash-object-changed-by-csv-library" class="external">http://stackoverflow.com/questions/6759487/ruby-hash-object-changed-by-csv-library</a> )</p> Ruby master - Feature #3714 (Closed): Add getters for Enumeratorhttps://bugs.ruby-lang.org/issues/37142010-08-19T04:51:48Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
Given an enumerator, there is no way to know what receiver, method and arguments it is based upon (although one could use Enumerator#inspect and parse it to get the method).</p>
<p>For sake of completeness and introspection, it could be useful to have access to them.</p>
<p>This patch adds Enumerator#receiver, #method and #arguments: <a href="http://github.com/marcandre/ruby/commit/fd4c17f7fd" class="external">http://github.com/marcandre/ruby/commit/fd4c17f7fd</a></p>
<p>Note that for Enumerator created with a block like Enumerator.new{|y| y << 1 << 2}, then receiver is a Enumerator::Generator, method is :each and arguments is []. This is consistent with inspect.</p>
<p>Thanks.<br>
=end</p> Ruby master - Bug #3434 (Closed): Specs for coercion?https://bugs.ruby-lang.org/issues/34342010-06-13T15:29:24Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
What are the official specs of coercion for mathematical classes?</p>
<p>I will take Matrix as an example, but my questions are meant to be for the general case.</p>
<p>My understanding is that for a <code>obj.coerce(obj_2)</code> should return <code>[compatible_2, compatible]</code> such that <code>compatible2.send(some_operation, compatible)</code> returns (if possible) a meaningful result.</p>
<p>Can we assume anything more about coercion? I'm asking because I find in test_matrix (@m1 being a Matrix):</p>
<p>def test_scalar_mul<br>
s1 = @m1.coerce(1).first<br>
assert_equal(Matrix[[1]], (s1 * 1) * Matrix[[1]])<br>
assert_equal(Vector[2], s1 * Vector[2])<br>
assert_equal(Matrix[[2]], s1 * Matrix[[2]])<br>
o = Object.new<br>
def o.coerce(x)<br>
[1, 1]<br>
end<br>
assert_equal(1, s1 * o)<br>
end</p>
<ol>
<li>Should the first and last assert work? Are implementers of other mathematical classes mandated/encouraged to provide that level of compatibility? Are users of <code>coerce</code> encouraged (or guaranteed success) when doing any other operation than <code>compatible2 * compatible</code> or similar?</li>
</ol>
<p>My feeling is that the only thing one should do with the results of coerce is to call an operation on the first element and pass the second element. Doing operations using only one of the two returned elements with another new object should yield undetermined results.</p>
<p>If this is the case, I feel the Matrix library would be best to assume that Scalar#* is called with a Matrix or Vector (remember that the Scalar is not meant to be used directly by anybody else than the Matrix lib), and these two assert would <em>not</em> work.</p>
<ol start="2">
<li>
<p>The second assert is clearly implementation dependant. In the current implementation, both Vector and Matrix use Matrix::Scalar as a temporary class, but that could change.</p>
</li>
<li>
<p>I am assuming that compatible doesn't have to be equal?, eql? or == to obj nor of the same class, and the same is true for compatible_2 vs obj_2, right? Thus the third assert isn't a spec guaranteed for all implementations.</p>
</li>
<li>
<p>Finally, a somewhat trivial question: should <code>obj.coerce(obj_2)</code> succeed if <code>obj</code> and <code>obj_2</code> are of the same class (and presumably return <code>[obj_2, obj]</code>)? Clearly this is not very useful, but the specs should be made precise.</p>
</li>
</ol>
<p>This is the case for Numeric and is explicit in the documentation but fails for Matrix:</p>
<pre><code> Matrix.I(2).coerce(Matrix.I(2)) # => TypeError: Matrix can't be coerced into Matrix
</code></pre>
<p>Coercion should always work for the trivial case of two objects of the same class, right?</p>
<a name="Thanks"></a>
<h2 >Thanks.<a href="#Thanks" class="wiki-anchor">¶</a></h2>
<p>Marc-André<br>
=end</p> Ruby master - Bug #3352 (Rejected): Delegates: protected methodshttps://bugs.ruby-lang.org/issues/33522010-05-27T15:55:26Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
require 'delegate'</p>
<pre><code> class X
protected
def pro
:foo
end
end
obj = X.new
obj.pro #=> NoMethodError: protected method `pro' called for #<X:0x000001008a2a68>
SimpleDelegator.new(obj).pro #=> :foo
</code></pre>
<p>I feel it would be more sensible to raise a NoMethodError.</p>
<p>No test seem to be testing for protected access, nor does RubySpec.</p>
<p>Unless there is objection, I'll commit the following:</p>
<p>diff --git a/lib/delegate.rb b/lib/delegate.rb<br>
index f366091..93fbc37 100644<br>
--- a/lib/delegate.rb<br>
+++ b/lib/delegate.rb<br>
@@ -141,7 +141,7 @@ class Delegator < BasicObject<br>
def method_missing(m, *args, &block)<br>
target = self.<strong>getobj</strong><br>
begin</p>
<ul>
<li>
<pre><code> target.respond_to?(m) ? target.__send__(m, *args, &block) : super(m, *args, &block)
</code></pre>
</li>
</ul>
<ul>
<li>
<pre><code> target.respond_to?(m) ? target.public_send(m, *args, &block) : super
</code></pre>
ensure<br>
$@.delete_if {|t| %r"\A#{Regexp.quote(<strong>FILE</strong>)}:#{<strong>LINE</strong>-2}:"o =~ t} if $@<br>
end<br>
=end</li>
</ul> Ruby master - Bug #3350 (Closed): Protected methods & documentationhttps://bugs.ruby-lang.org/issues/33502010-05-27T15:28:46Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
The official doc currently states that Object#methods "returns a list of the names of methods publicly accessible".</p>
<p>Similarly, Module#instance_methods states that it returns "the public methods" of the Module. Note that the doc was modified in r19900, but I feel the wording "instance method that is callable from outside" still implies public only.</p>
<p>The current behavior doesn't fit the doc, since protected methods are also matched:</p>
<pre><code> class X
def protected_method; end
protected :protected_method
end
X.new.methods.include?(:protected_method) #=> true
X.instance_methods.include?(:protected_method) #=> true
</code></pre>
<p>The documentation for Module#method_defined? on the other states it matches public and protected methods.</p>
<p>Should I change the doc to reflect the current behavior, as per the patch below?</p>
<p>I'm asking in part because I lack experience with protected methods and I am surprised by the fact that protected methods are matched by these "default" methods.</p>
<p>The fact that Object#respond_to? also matches protected is even less practical since there is no "public-only" equivalent. I might be mistaken, but I believe that the only way of knowing if obj responds publicly to :foo is to do something like</p>
<pre><code> publicly_responds = obj.public_method(:foo) rescue false
</code></pre>
<p>I dislike the fact that obj.respond_to?(:foo) && obj.foo might raise a NoMethodError.</p>
<p>It looks like this was discussed a in [ruby_dev:40461], but I don't know the outcome.</p>
<p>I'm curious: what examples exist where one would want to match public and protected methods but not private ones?</p>
<p>Matz, is there any chance the handling of #respond_to?, #methods, etc..., with regards to protected methods will change (in 1.9.2 or later)?</p>
<p>--<br>
Marc-André</p>
<p>diff --git a/class.c b/class.c<br>
index c586f7a..683fa7b 100644<br>
--- a/class.c<br>
+++ b/class.c<br>
@@ -865,8 +865,8 @@ class_instance_method_list(int argc, VALUE *argv, VALUE mod, int (*func) (ID, lo</p>
<ul>
<li>call-seq:</li>
<li>
<pre><code>mod.instance_methods(include_super=true) -> array
</code></pre>
</li>
<li>
</ul>
<ul>
<li>
<ul>
<li>Returns an array containing the names of instance methods that is callable</li>
</ul>
</li>
<li>
<ul>
<li>from outside in the receiver. For a module, these are the public methods;</li>
</ul>
</li>
</ul>
<ul>
<li>
<ul>
<li>Returns an array containing the names of the public and protected instance</li>
</ul>
</li>
<li>
<ul>
<li>methods in the receiver. For a module, these are the public and protected methods;</li>
<li>for a class, they are the instance (not singleton) methods. With no</li>
<li>argument, or with an argument that is <code>false</code>, the</li>
<li>instance methods in <i>mod</i> are returned, otherwise the methods<br>
@@ -954,6 +954,7 @@ rb_class_public_instance_methods(int argc, VALUE *argv, VALUE mod)</li>
<li>Returns an array of the names of singleton methods for <i>obj</i>.</li>
<li>If the optional <i>all</i> parameter is true, the list will include</li>
<li>methods in modules included in <i>obj</i>.</li>
</ul>
</li>
<li>
<ul>
<li>Only public and protected singleton methods are returned.</li>
<li>
<li>
<pre><code>module Other
</code></pre>
</li>
<li>
<pre><code> def three() end
</code></pre>
</li>
</ul>
</li>
</ul>
<p>diff --git a/object.c b/object.c<br>
index a7f05a1..351d16f 100644<br>
--- a/object.c<br>
+++ b/object.c<br>
@@ -1755,7 +1755,7 @@ rb_mod_const_defined(int argc, VALUE *argv, VALUE mod)</p>
<ul>
<li>call-seq:</li>
<li>
<pre><code>obj.methods -> array
</code></pre>
</li>
<li>
</ul>
<ul>
<li>
<ul>
<li>Returns a list of the names of methods publicly accessible in</li>
</ul>
</li>
</ul>
<ul>
<li>
<ul>
<li>Returns a list of the names of public and protected methods of</li>
<li>
<i>obj</i>. This will include all the methods accessible in</li>
<li>
<i>obj</i>'s ancestors.</li>
<li>
</ul>
</li>
</ul>
<p>=end</p> Ruby master - Bug #3225 (Closed): lib/uri/mailto.rb slightly wrong regexphttps://bugs.ruby-lang.org/issues/32252010-04-30T13:24:56Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
Looking for example of the 3rd parameter of value 'N' for Regexp.new, I noticed in lib/uri/mailto.rb</p>
<p>HEADER_REGEXP = Regexp.new(HEADER_PATTERN, 'N').freeze</p>
<p>Unless I'm mistaken, the 'N' in second position is simply a flag for case insensitivity, but HEADER_PATTERN already is, so it should either be</p>
<p>HEADER_REGEXP = Regexp.new(HEADER_PATTERN).freeze</p>
<p>or</p>
<p>HEADER_REGEXP = Regexp.new(HEADER_PATTERN, nil, 'N').freeze</p>
<p>but I don't know which one...<br>
=end</p> Ruby master - Bug #3224 (Closed): Regexp.new("...", nil, "n") is not documentedhttps://bugs.ruby-lang.org/issues/32242010-04-30T13:15:06Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
While fixing the error messages for the number of parameters ( r27558 ), I noticed that Regexp.new accepts a third undocumented parameter.</p>
<p>Is it supported? If so it should be documented.</p>
<p>I also note that test_regexp.rb tests for the third parameter being "u", even though this has no effect. Maybe these tests should be removed?<br>
=end</p> Ruby master - Feature #3222 (Closed): Can bignums have singleton class & methods?https://bugs.ruby-lang.org/issues/32222010-04-30T07:07:08Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
Fixing up the rubyspecs led me to the following:</p>
<p>bn = 1 << 100<br>
class << bn<br>
def foo<br>
42<br>
end<br>
end</p>
<a name="gt-TypeError-cant-define-singleton-method-foo-for-Bignum"></a>
<h1 >=> TypeError: can't define singleton method "foo" for Bignum<a href="#gt-TypeError-cant-define-singleton-method-foo-for-Bignum" class="wiki-anchor">¶</a></h1>
<p>bn.define_singleton_method(:foo){42}</p>
<a name="gt-TypeError-cant-define-singleton-method-foo-for-Bignum-2"></a>
<h1 >=> TypeError: can't define singleton method "foo" for Bignum<a href="#gt-TypeError-cant-define-singleton-method-foo-for-Bignum-2" class="wiki-anchor">¶</a></h1>
<p>On the other hand...</p>
<p>module Bar<br>
def foo<br>
42<br>
end<br>
end<br>
class << bn<br>
include Bar<br>
end<br>
bn.foo # => 42</p>
<p>If Ruby won't allow singleton methods for Bignum, then shouldn't it disallow access to the singleton class completely?</p>
<p>See also issue <a class="issue tracker-1 status-5 priority-4 priority-default closed" title="Bug: an instance of Bignum can have singleton methods (Closed)" href="https://bugs.ruby-lang.org/issues/601">#601</a><br>
=end</p> Ruby master - Bug #3192 (Closed): Missing Set#keep_if, select! [patch]https://bugs.ruby-lang.org/issues/31922010-04-23T14:40:16Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
Set (& SortedSet) were forgotten when adding keep_if and select!</p>
<p>Unless there is objection, I'll commit the attached patch (and add tests to RubySpec).<br>
=end</p> Ruby master - Bug #3169 (Closed): RDoc crossref confused by instance and class methods having sam...https://bugs.ruby-lang.org/issues/31692010-04-19T09:39:40Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
The documentation for the two methods below will both have a reference to X.foo (which appeared first). The "See X#foo" should reference to the instance method instead.</p>
<p>class X<br>
# The class method. See X#foo<br>
def self.foo<br>
end</p>
<pre><code> # The instance method. See X.foo
def foo
end
</code></pre>
<p>end<br>
=end</p> Ruby master - Bug #3104 (Closed): Random: seeding issueshttps://bugs.ruby-lang.org/issues/31042010-04-07T15:08:35Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
I think there are a couple of very small errors with the seeding of the Random class.</p>
<ol>
<li>
<p>Seeding sometimes ignores the high bit of seed data.</p>
<p>Random.new((1<<64)-1).rand == Random.new((1 << 65) -1).rand</p>
<a name="gt-true-should-be-false"></a>
<h1 >=> true, should be false<a href="#gt-true-should-be-false" class="wiki-anchor">¶</a></h1>
</li>
</ol>
<p>Probably some leftover code?</p>
<p>diff --git a/random.c b/random.c<br>
index 02d081c..447e59f 100644<br>
--- a/random.c<br>
+++ b/random.c<br>
@@ -411,8 +411,6 @@ rand_init(struct MT *mt, VALUE vseed)<br>
init_genrand(mt, buf[0]);<br>
}<br>
else {</p>
<ul>
<li>
<pre><code> if (buf[len-1] == 1) /* remove leading-zero-guard */
</code></pre>
</li>
<li>
<pre><code> len--;
init_by_array(mt, buf, len);
</code></pre>
}<br>
if (buf != buf0) xfree(buf);</li>
</ul>
<ol start="2">
<li>
<p>Treatment of negative seed values is currently platform dependent for all negative fixnums. From the same negative seed, the random sequence can be different on 32 bit platforms than on 64 bit ones.</p>
<p>Random.new(-1).rand == Random.new((1<<63) -1).rand</p>
<a name="gt-true-on-64-bit-platform-false-on-32-bit"></a>
<h1 >=> true on 64 bit platform, false on 32 bit<a href="#gt-true-on-64-bit-platform-false-on-32-bit" class="wiki-anchor">¶</a></h1>
<p>Random.new(-1).rand == Random.new((1<<31) -1).rand</p>
<a name="gt-false-on-64-bit-platform-true-on-32-bit"></a>
<h1 >=> false on 64 bit platform, true on 32 bit<a href="#gt-false-on-64-bit-platform-true-on-32-bit" class="wiki-anchor">¶</a></h1>
</li>
</ol>
<p>The simple solution below ignores the sign (which is what's done in case of negative Bignum). This means that the same values will result from seeding with x or -x. Another solution would be to use the lowest bit for the sign (for both fixnums and bignums) so as to avoid this collision, or raise an error in case of negative seeds.</p>
<p>diff --git a/random.c b/random.c<br>
index 02d081c..272f7b5 100644<br>
--- a/random.c<br>
+++ b/random.c<br>
@@ -367,6 +367,7 @@ rand_init(struct MT *mt, VALUE vseed)<br>
{<br>
volatile VALUE seed;<br>
long blen = 0;</p>
<ul>
<li>long fixnum_seed;<br>
int i, j, len;<br>
unsigned int buf0[SIZEOF_LONG / SIZEOF_INT32 * 4], *buf = buf0;</li>
</ul>
<p>@@ -374,9 +375,11 @@ rand_init(struct MT *mt, VALUE vseed)<br>
switch (TYPE(seed)) {<br>
case T_FIXNUM:<br>
len = 1;</p>
<ul>
<li>
<pre><code> buf[0] = (unsigned int)(FIX2ULONG(seed) & 0xffffffff);
</code></pre>
</li>
</ul>
<ul>
<li>
<pre><code> fixnum_seed = FIX2LONG(seed);
</code></pre>
</li>
<li>
<pre><code> if (fixnum_seed < 0) fixnum_seed = -fixnum_seed;
</code></pre>
</li>
<li>
<pre><code> buf[0] = (unsigned int)(fixnum_seed & 0xffffffff);
</code></pre>
</li>
</ul>
<p>#if SIZEOF_LONG > SIZEOF_INT32</p>
<ul>
<li>
<pre><code> if ((buf[1] = (unsigned int)(FIX2ULONG(seed) >> 32)) != 0) ++len;
</code></pre>
</li>
</ul>
<ul>
<li>
<pre><code> if ((buf[1] = (unsigned int)(fixnum_seed >> 32)) != 0) ++len;
</code></pre>
</li>
</ul>
<p>#endif<br>
break;<br>
case T_BIGNUM:</p>
<ol start="3">
<li>Finally, I was wondering if there wasn't a typo trashing some of the bits in the initial states when seeding with an array:</li>
</ol>
<p>diff --git a/random.c b/random.c<br>
index 02d081c..f3db095 100644<br>
--- a/random.c<br>
+++ b/random.c<br>
@@ -151,7 +151,7 @@ init_by_array(struct MT *mt, unsigned int init_key[], int key_length)<br>
if (i>=N) { mt->state[0] = mt->state[N-1]; i=1; }<br>
}</p>
<ul>
<li>mt->state[0] = 0x80000000U; /* MSB is 1; assuring non-zero initial array */</li>
</ul>
<ul>
<li>mt->state[0] |= 0x80000000U; /* MSB is 1; assuring non-zero initial array */<br>
}</li>
</ul>
<p>static void</p>
<p>These changes will surely create test failures which should be trivial to fix.<br>
=end</p> Ruby master - Feature #3023 (Closed): RDoc urlshttps://bugs.ruby-lang.org/issues/30232010-03-27T06:08:35Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
Currently, rdoc generates url for methods that look like:</p>
<p>.../classes/Enumerable.html#M002713</p>
<p>This has two drawbacks.</p>
<ol>
<li>
<p>This URL is subject to change when methods are added/removed. This makes it impossible to reference the documentation of a method.</p>
</li>
<li>
<p>There is no way to know what method this refers to just by looking at it.</p>
</li>
</ol>
<p>If instead the url looked like:</p>
<p>.../classes/Enumerable.html#map-instance_method</p>
<p>then it would be human readable and RESTful.</p>
<p>The method name simply need to be URI encoded and suffixed with either instance_method, class_method, or -constant.<br>
=end</p> Ruby master - Bug #3022 (Closed): What are $. and ARGF.lineno ?https://bugs.ruby-lang.org/issues/30222010-03-27T04:13:59Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin</p>
<ol>
<li>$. is not officially documented.</li>
</ol>
<p>This makes it difficult to know if it works as expected or not...</p>
<p>The Ruby Prog Language (Flanagan & Matz) states:<br>
"The number of the last line read from the current input file. Equivalent to ARGF.lineno."</p>
<p>This is not true in trunk, as demonstrated by:</p>
<p>$ rubydev -e "<br>
ARGF.gets<br>
File.open('/etc/passwd'){|f| f.gets; f.gets}<br>
p $., ARGF.lineno<br>
" /etc/hosts<br>
2<br>
1</p>
<p>What is the "current input file"? Not clear, but it's not thread local, as shown:</p>
<p>$ rubydev -e "<br>
p $.<br>
Thread.new{File.open('/etc/passwd').gets; p $. }.join;<br>
p $.<br>
"<br>
0<br>
1<br>
1</p>
<ol start="2">
<li>ARGF.lineno does not conform to its doc.</li>
</ol>
<p>The doc states:</p>
<p>Returns the current line number of the current file in ARGF. This value<br>
can be set manually with ARGF.lineno=.</p>
<p>Reading this, I would expect ARGF.lineno to be the same as ARGF.to_io.lineno.</p>
<p>That is not the case:</p>
<p>rubydev -e 'p "#{ARGF.lineno} #{ARGF.to_io.lineno}" while ARGF.gets' /etc/hosts /etc/passwd<br>
"1 1"<br>
"2 2"<br>
...<br>
"25 1"<br>
"26 2"<br>
...</p>
<ol>
<li>
<p>Maybe the best definition would be that $. returns the number of line read operations issued, from the last time an IO was read in the current thread?</p>
</li>
<li>
<p>I suggest the documentation of ARGF.lineno be changed to:</p>
</li>
</ol>
<p>Returns the current line number of ARGF as a whole. This value<br>
can be set manually with ARGF.lineno=.</p>
<p>See also <a href="https://blade.ruby-lang.org/ruby-core/26303">[ruby-core:26303]</a><br>
=end</p> Ruby master - Feature #2832 (Closed): Vector#each and Enumerablehttps://bugs.ruby-lang.org/issues/28322010-03-02T18:04:44Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
Vector should implement #each and include Enumerable, since it is so Array-like.</p>
<p>Enumerable methods that return an array should probably be specialized to return a Vector (e.g. map, first, ...)</p>
<p>See also <a href="https://blade.ruby-lang.org/ruby-core/28403">[ruby-core:28403]</a>, [redmine:2831]<br>
=end</p> Ruby master - Feature #2831 (Closed): Matrix: each, all?, none?, each_with_index, ....https://bugs.ruby-lang.org/issues/28312010-03-02T17:10:01Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
There is Matrix#collect, but it would be useful to have Matrix#each and include Enumerable.</p>
<p>All methods that return anything else than an array are completely natural: Matrix#all?, Matrix#any?, Matrix#count, Matrix#none?, etc...</p>
<p>Others can be left alone or undefined (#sort, #drop, ...)</p>
<p>Matrix#each_with_index should go over all values, and yield value, row and column</p>
<p>Currently, some basic operations are quite a bit more difficult than they could be. An example: to check if a matrix is composed of Integer:</p>
<a name="Currently-need-to-build-an-intermediate-array"></a>
<h1 >Currently, need to build an intermediate array<a href="#Currently-need-to-build-an-intermediate-array" class="wiki-anchor">¶</a></h1>
<p>m = Matrix[...]<br>
integral = m.row_vectors.all? do |row|<br>
row.all? &:integer?<br>
end</p>
<a name="Could-be"></a>
<h1 >Could be<a href="#Could-be" class="wiki-anchor">¶</a></h1>
<p>integral = m.all? &:integer?</p>
<p>I'll gladly provide precisions on each method and make a patch.<br>
=end</p> Ruby master - Bug #2830 (Closed): Some methods raise ArgumentError instead of TypeErrorhttps://bugs.ruby-lang.org/issues/28302010-03-02T13:45:08Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
Some methods of Ruby 1.9 expect integers/reals and call internally nurat_int_value/nurat_int_check. These functions raise an ArgumentError when the argument is not an Integer, instead of a TypeError.</p>
<p>Thus:<br>
42.gcd(:foo) # => ArgumentError, should be TypeError<br>
42.lcm(:foo) # => ditto<br>
42.gcdlcm(:foo) # => ditto<br>
Rational(:foo,1) # => ditto</p>
<p>Note that on the other hand:<br>
Rational(nil, 1) # => TypeError<br>
Rational(:foo) # => TypeError</p>
<p>In a similar fashion:<br>
Complex.rect(nil) # => ArgumentError, should be TypeError<br>
Complex.polar(nil) # => ditto</p>
<p>Unless there is objection, I will commit the following patch (and fix RubySpec):</p>
<p>diff --git a/complex.c b/complex.c<br>
index 214d3a2..6742257 100644<br>
--- a/complex.c<br>
+++ b/complex.c<br>
@@ -377,7 +377,7 @@ nucomp_real_check(VALUE num)<br>
break;<br>
default:<br>
if (!k_numeric_p(num) || !f_real_p(num))</p>
<ul>
<li>
<pre><code> rb_raise(rb_eArgError, "not a real");
</code></pre>
</li>
</ul>
<ul>
<li>
<pre><code> rb_raise(rb_eTypeError, "not a real");
</code></pre>
}<br>
}</li>
</ul>
<p>diff --git a/rational.c b/rational.c<br>
index 65d3cf4..f5a6d26 100644<br>
--- a/rational.c<br>
+++ b/rational.c<br>
@@ -419,7 +419,7 @@ nurat_int_check(VALUE num)<br>
break;<br>
default:<br>
if (!k_numeric_p(num) || !f_integer_p(num))</p>
<ul>
<li>
<pre><code> rb_raise(rb_eArgError, "not an integer");
</code></pre>
</li>
</ul>
<ul>
<li>
<pre><code> rb_raise(rb_eTypeError, "not an integer");
</code></pre>
}<br>
}<br>
=end</li>
</ul> Ruby master - Bug #2829 (Closed): Missing documentation for Exception subclasses.https://bugs.ruby-lang.org/issues/28292010-03-02T13:38:53Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
I noticed that the documentation for Exception subclasses are either that of Exception itself, or just plain non-sensical because they are actually missing (see ZeroDivisionError, FiberError, ...)</p>
<p>It would be preferable if the Exception classes were at least briefly described.</p>
<p>I've made a first draft of a description for all Exception subclasses, and would encourage anyone interested in improving it by editing the wiki:</p>
<p><a href="http://redmine.ruby-lang.org/wiki/ruby/ExceptionClassesDoc" class="external">http://redmine.ruby-lang.org/wiki/ruby/ExceptionClassesDoc</a></p>
<p>Thanks.<br>
=end</p> Ruby master - Feature #2772 (Closed): Matrix: Calculating determinant using Bareiss algorithm [pa...https://bugs.ruby-lang.org/issues/27722010-02-21T06:16:21Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
Yu Ichino suggested to use a different algorithm to calculate the determinant of a matrix in <a href="https://blade.ruby-lang.org/ruby-core/28166">[ruby-core:28166]</a>.</p>
<p>I second his proposal. To reduce the risk of this proposal to go without a response, I am creating this request.</p>
<p>Bareiss' algorithm has two big advantages:</p>
<ul>
<li>can calculate the determinant of integer matrices using only integer intermediate results.</li>
<li>has minimal requirements on the number of bits required for intermediate results, thus reducing the floating point rounding errors.</li>
</ul>
<p>Performance-wise, it has the same complexity O(n^3) as the current traditional method.<br>
For Integer matrices, there is a big gain (say about 15 times faster) because the traditional version must use #quo and rationals, while the Bareiss algorithm can use #/ and keep the intermediate results as a Fixnum. The same goes for Bignum, of course (where the gain is even bigger).<br>
For float version, the Bareiss algorithm is slightly slower (~33%):</p>
<pre><code> user system total real
</code></pre>
<p>Fixnum: det 8.660000 0.010000 8.670000 ( 8.672686)<br>
Fixnum: det_bareiss 0.590000 0.000000 0.590000 ( 0.594747)<br>
Float: det 0.160000 0.000000 0.160000 ( 0.154779)<br>
Float: det_bareiss 0.240000 0.000000 0.240000 ( 0.237806)</p>
<p>I have revised Yu's code, and attached a patch.</p>
<p>I'm also attaching the performance test I used, for anyone interested.<br>
=end</p> Ruby master - Feature #2771 (Closed): Matrix: constructor to build with blockhttps://bugs.ruby-lang.org/issues/27712010-02-21T06:10:56Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
I believe the following simple constructor could be a helpful addition to matrix.rb</p>
<p>class Matrix</p>
<h1></h1>
<a name="Creates-a-matrix-of-row_size-x-column_size"></a>
<h1 >Creates a matrix of +row_size+ x +column_size+.<a href="#Creates-a-matrix-of-row_size-x-column_size" class="wiki-anchor">¶</a></h1>
<a name="It-fills-the-values-by-calling-the-given-block"></a>
<h1 >It fills the values by calling the given block,<a href="#It-fills-the-values-by-calling-the-given-block" class="wiki-anchor">¶</a></h1>
<a name="passing-the-current-row-and-column"></a>
<h1 >passing the current row and column.<a href="#passing-the-current-row-and-column" class="wiki-anchor">¶</a></h1>
<h1></h1>
<a name="m-Matrixbuild2-4-row-col-col-row-"></a>
<h1 >m = Matrix.build(2, 4) {|row, col| col - row }<a href="#m-Matrixbuild2-4-row-col-col-row-" class="wiki-anchor">¶</a></h1>
<a name="gt-Matrix0-1-2-3-1-0-1-2"></a>
<h1 >=> Matrix[[0, 1, 2, 3], [-1, 0, 1, 2]]<a href="#gt-Matrix0-1-2-3-1-0-1-2" class="wiki-anchor">¶</a></h1>
<a name="m-Matrixbuild3-rand-"></a>
<h1 >m = Matrix.build(3) { rand }<a href="#m-Matrixbuild3-rand-" class="wiki-anchor">¶</a></h1>
<a name="gt-a-3x3-matrix-with-random-elements"></a>
<h1 >=> a 3x3 matrix with random elements<a href="#gt-a-3x3-matrix-with-random-elements" class="wiki-anchor">¶</a></h1>
<h1></h1>
<p>def self.build(row_size, column_size = row_size)<br>
raise ArgumentError if row_size < 0 || column_size < 0<br>
return to_enum :build, row_size, column_size unless block_given?<br>
rows = row_size.times.map do |i|<br>
column_size.times.map do |j|<br>
yield i, j<br>
end<br>
end<br>
new rows, column_size<br>
end<br>
end<br>
=end</p> Ruby master - Bug #2770 (Closed): Matrix: determinant for rectangular matrices should raise an er...https://bugs.ruby-lang.org/issues/27702010-02-21T04:46:28Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
Keiju,</p>
<p>$ rubydev -r matrix -e "puts Matrix[[1], [2], [3]].determinant"<br>
0</p>
<p>This corresponds to the documentation, but is not mathematically valid, since the determinant for rectangular matrices is not defined (at least for now! <a href="http://bit.ly/bwW7Gs" class="external">http://bit.ly/bwW7Gs</a> )</p>
<p>I believe an error should be thrown, similar to:<br>
$ rubydev -r matrix -e "puts Matrix[[1], [2], [3]].trace"<br>
/usr/local/rubydev/lib/ruby/1.9.1/matrix.rb:837:in <code>trace': Matrix dimension mismatch (ExceptionForMatrix::ErrDimensionMismatch) from -e:1:in </code>'</p>
<p>Since this is an API change, I attached a patch and will wait for your approval before committing.<br>
=end</p> Ruby master - Bug #2525 (Closed): URI#normalize incompletehttps://bugs.ruby-lang.org/issues/25252009-12-24T16:38:28Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
"<a href="hTTp://example.com/" class="external">hTTp://example.com/</a>" and "<a href="http://exa%4dple.com/" class="external">http://exa%4dple.com/</a>" should both be normalized to "<a href="http://example.com/" class="external">http://example.com/</a>" as per RFC 3986.</p>
<p>They currently are not and thus not considered ==.</p>
<p>Tests added to RubySpec<br>
=end</p> Ruby master - Bug #2499 (Closed): InstructionSequence.dissassemble can crashhttps://bugs.ruby-lang.org/issues/24992009-12-19T11:52:54Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
I was trying to understand what InstructionSequence.disassemble is supposed to do when:</p>
<p>$ rubydev -e 'RubyVM::InstructionSequence.disassemble("x".method(:upcase))'<br>
-e:1: [BUG] Segmentation fault<br>
ruby 1.9.2dev (2009-12-19 trunk 26121) [x86_64-darwin10.2.0]</p>
<h2>-- control frame ----------<br>
c:0004 p:---- s:0010 b:0010 l:000009 d:000009 CFUNC :disassemble<br>
c:0003 p:0027 s:0006 b:0006 l:0022c8 d:001e98 EVAL -e:1<br>
c:0002 p:---- s:0004 b:0004 l:000003 d:000003 FINISH<br>
c:0001 p:0000 s:0002 b:0002 l:0022c8 d:0022c8 TOP</h2>
<p>-e:1:in <code><main>' -e:1:in </code>disassemble'</p>
<p>-- C level backtrace information -------------------------------------------</p>
<p>[NOTE]<br>
You may have encountered a bug in the Ruby interpreter or extension libraries.<br>
Bug reports are welcome.<br>
For details: <a href="http://www.ruby-lang.org/bugreport.html" class="external">http://www.ruby-lang.org/bugreport.html</a></p>
<p>Abort trap<br>
=end</p> Ruby master - Bug #2496 (Closed): Delegate: #methods and #public_methods should return delegated ...https://bugs.ruby-lang.org/issues/24962009-12-19T10:38:20Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
require 'delegate'<br>
s = SimpleDelegator.new "Hello, world!"<br>
s.respond_to? :upcase # => true<br>
s.method :upcase # => #<Method : SimpleDelegator#upcase><br>
s.methods.include? :upcase # => false, true expected</p>
<p>Similar problem with public_methods.<br>
I propose that they return the union of methods from the delegator object and the methods of the object delegated to (only the public ones, since other ones are not forwarded)</p>
<p>Patch:<br>
diff --git a/lib/delegate.rb b/lib/delegate.rb<br>
index 77804e4..2fd5b49 100644<br>
--- a/lib/delegate.rb<br>
+++ b/lib/delegate.rb<br>
@@ -158,6 +158,22 @@ class Delegator<br>
end</p>
<pre><code>#
</code></pre>
<ul>
<li>
<a name="Returns-the-methods-available-to-this-delegate-object-as-the-union"></a>
<h1 >Returns the methods available to this delegate object as the union<a href="#Returns-the-methods-available-to-this-delegate-object-as-the-union" class="wiki-anchor">¶</a></h1>
</li>
<li>
<a name="of-this-objects-methods-and-the-public-methods-of-__getobj__"></a>
<h1 >of this object's methods and the public methods of __getobj__.<a href="#of-this-objects-methods-and-the-public-methods-of-__getobj__" class="wiki-anchor">¶</a></h1>
</li>
<li>
<h1></h1>
</li>
<li>def methods</li>
<li>self.<strong>getobj</strong>.public_methods | super</li>
<li>end</li>
<li>
<li>
<h1></h1>
</li>
<li>
<a name="Returns-the-methods-available-to-this-delegate-object-as-the-union-of-this-object"></a>
<h1 >Returns the methods available to this delegate object as the union of this object<a href="#Returns-the-methods-available-to-this-delegate-object-as-the-union-of-this-object" class="wiki-anchor">¶</a></h1>
</li>
<li>
<a name="and-the-methods-of-__getobj__"></a>
<h1 >and the methods of __getobj__.<a href="#and-the-methods-of-__getobj__" class="wiki-anchor">¶</a></h1>
</li>
<li>
<h1></h1>
</li>
<li>def public_methods(all=true)</li>
<li>self.<strong>getobj</strong>.public_methods(all) | super</li>
<li>end</li>
<li>
<li>
<h1></h1>
<a name="Returns-true-if-two-objects-are-considered-same"></a>
<h1 >Returns true if two objects are considered same.<a href="#Returns-true-if-two-objects-are-considered-same" class="wiki-anchor">¶</a></h1>
<h1></h1>
def ==(obj)<br>
=end</li>
</ul> Ruby master - Bug #2495 (Closed): Matrix: Vector#each2 should check its argumenthttps://bugs.ruby-lang.org/issues/24952009-12-19T10:35:22Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
$ rubydev -r matrix -e 'p Vector[*1..4].each2(nil){|x, y| p "#{x}, #{y}"}'<br>
/usr/local/rubydev/lib/ruby/1.9.1/matrix.rb:1149:in <code>each2': undefined method </code>size' for nil:NilClass (NoMethodError)</p>
<p>$ rubydev -r matrix -e 'p Vector[*1..4].each2(42){|x, y| p "#{x}, #{y}"}'<br>
/usr/local/rubydev/lib/ruby/1.9.1/matrix.rb:1149:in <code>each2': Vector dimension mismatch (ExceptionForMatrix::ErrDimensionMismatch) from -e:1:in </code>'</p>
<p>$ rubydev -r matrix -e 'p Vector[*1..8].each2(42){|x, y| p "#{x}, #{y}"}'<br>
"1, 0"<br>
"2, 1"<br>
"3, 0"<br>
"4, 1"<br>
"5, 0"<br>
"6, 1"<br>
"7, 0"<br>
"8, 0"</p>
<p>(or vice versa, if on a 32 bit platform)<br>
=end</p> Ruby master - Bug #2365 (Closed): Matrix: poor handling of coercion errors [patch]https://bugs.ruby-lang.org/issues/23652009-11-14T10:35:04Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
rubydev -r matrix -e 'p Matrix.I(2) - nil'<br>
/usr/local/rubydev/lib/ruby/1.9.1/matrix.rb:533:in <code>-': undefined method </code>coerce' for nil:NilClass (NoMethodError)<br>
from -e:1:in `'</p>
<p>Expected:<br>
some_where_in_the/matrix.rb:in <code>-': nil can't be coerced into Matrix (TypeError) from -e:1:in </code>'</p>
<p>Same situation with Vector.</p>
<p>I'd be grateful if you could confirm the attached patch is ok.<br>
=end</p> Ruby master - Bug #2330 (Closed): Non systematic segmentation fault with autoload rubyspechttps://bugs.ruby-lang.org/issues/23302009-11-04T14:36:10Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
Starting from r25601, the following rubyspec will crash about half of the time (just repeat a couple of times):</p>
<p>$ mspec -t rubydev core/kernel/autoload_spec.rb<br>
ruby 1.9.2dev (2009-11-04 trunk 25641) [x86_64-darwin10.0.0]<br>
.........../Users/work/mspec/lib/mspec/mocks/proxy.rb:8: [BUG] Segmentation fault<br>
ruby 1.9.2dev (2009-11-04 trunk 25641) [x86_64-darwin10.0.0]</p>
<p>-- control frame ----------<br>
c:0036 p:0024 s:0121 b:0121 l:000120 d:000120 METHOD /Users/work/mspec/lib/mspec/mocks/proxy.rb:8<br>
c:0035 p:---- s:0115 b:0115 l:000114 d:000114 FINISH<br>
c:0034 p:---- s:0113 b:0113 l:000112 d:000112 CFUNC :autoload<br>
c:0033 p:0057 s:0108 b:0108 l:000208 d:000107 BLOCK /Users/work/ruby/dev/spec/rubyspec/core/kernel/autoload_spec.rb:105<br>
c:0032 p:---- s:0105 b:0105 l:000104 d:000104 FINISH<br>
c:0031 p:---- s:0103 b:0103 l:000102 d:000102 CFUNC :instance_eval<br>
c:0030 p:0017 s:0100 b:0100 l:000099 d:000099 METHOD /Users/work/mspec/lib/mspec/runner/mspec.rb:67<br>
c:0029 p:0021 s:0094 b:0094 l:000081 d:000093 BLOCK /Users/work/mspec/lib/mspec/runner/context.rb:135<br>
c:0028 p:---- s:0091 b:0091 l:000090 d:000090 FINISH<br>
c:0027 p:---- s:0089 b:0089 l:000084 d:000088 IFUNC :instance_variable_get<br>
c:0026 p:---- s:0087 b:0087 l:000086 d:000086 CFUNC :each<br>
c:0025 p:---- s:0085 b:0085 l:000084 d:000084 CFUNC :all?<br>
c:0024 p:0053 s:0082 b:0082 l:000081 d:000081 METHOD /Users/work/mspec/lib/mspec/runner/context.rb:135<br>
c:0023 p:0101 s:0076 b:0076 l:000065 d:000075 BLOCK /Users/work/mspec/lib/mspec/runner/context.rb:163<br>
c:0022 p:---- s:0071 b:0071 l:000070 d:000070 FINISH<br>
c:0021 p:---- s:0069 b:0069 l:000068 d:000068 CFUNC :each<br>
c:0020 p:0127 s:0066 b:0066 l:000065 d:000065 METHOD /Users/work/mspec/lib/mspec/runner/context.rb:155<br>
c:0019 p:0109 s:0063 b:0063 l:000062 d:000062 METHOD /Users/work/mspec/lib/mspec/runner/mspec.rb:36<br>
c:0018 p:0029 s:0056 b:0056 l:000055 d:000055 METHOD /Users/work/mspec/lib/mspec/runner/object.rb:11<br>
c:0017 p:0167 s:0049 b:0049 l:000208 d:000208 TOP /Users/work/ruby/dev/spec/rubyspec/core/kernel/autoload_spec.rb:69<br>
c:0016 p:---- s:0047 b:0047 l:000046 d:000046 FINISH<br>
</p>
<p>mspec version: 1.5.12<br>
rubyspec: current<br>
=end</p> Ruby master - Bug #2311 (Closed): [BUG] cfp consistency error - sendhttps://bugs.ruby-lang.org/issues/23112009-10-30T12:15:55Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
Following r25521:</p>
<p>$rubydev -e '<br>
class X<br>
def method_missing(s); end<br>
end<br>
X.new.foo(:bar) rescue nil<br>
puts "hello"<br>
'</p>
<p>Generates<br>
-e:0:in <code>method_missing': wrong number of arguments (2 for 1) (ArgumentError) from -e:5:in </code>'<br>
Instead of "hello"</p>
<p>$ mspec core/file/stat/directory_spec.rb -t rubydev<br>
crashes ruby with [BUG] cfp consistency error - send<br>
=end</p> Ruby master - Feature #2266 (Closed): Matrix and Complex [patch]https://bugs.ruby-lang.org/issues/22662009-10-25T07:36:18Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
Now that Ruby 1.9 has Complex as a builtin type, it would be interesting if Matrix provided the following instance methods:<br>
#conj, alias conjugate<br>
#imag, alias imaginary<br>
#real,<br>
#real?,<br>
#rect, alias rectangular</p>
<p>Corresponding patch attached.</p>
<p>This patch could also be part of mathn in the ruby 1.8 branch.<br>
=end</p> Ruby master - Feature #2265 (Closed): Matrix#empty? [patch]https://bugs.ruby-lang.org/issues/22652009-10-25T07:33:01Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
Now that the Matrix library handles well empty matrices, how about a new instance method #empty?</p>
<p>diff --git a/lib/matrix.rb b/lib/matrix.rb<br>
index b577ef3..45ef25e 100644<br>
--- a/lib/matrix.rb<br>
+++ b/lib/matrix.rb<br>
@@ -386,6 +386,14 @@ class Matrix<br>
#++</p>
<pre><code>#
</code></pre>
<ul>
<li>
<a name="Returns-true-if-this-is-an-empty-matrix-ie-if-the-number-of-rows"></a>
<h1 >Returns +true+ if this is an empty matrix, i.e. if the number of rows<a href="#Returns-true-if-this-is-an-empty-matrix-ie-if-the-number-of-rows" class="wiki-anchor">¶</a></h1>
</li>
<li>
<a name="or-the-number-of-columns-is-0"></a>
<h1 >or the number of columns is 0.<a href="#or-the-number-of-columns-is-0" class="wiki-anchor">¶</a></h1>
</li>
<li>
<h1></h1>
</li>
<li>def empty?</li>
<li>column_size == 0 || row_size == 0</li>
<li>end</li>
<li>
<li>
<h1></h1>
<a name="Returns-true-if-this-is-a-regular-matrix"></a>
<h1 >Returns +true+ if this is a regular matrix.<a href="#Returns-true-if-this-is-a-regular-matrix" class="wiki-anchor">¶</a></h1>
<h1></h1>
</li>
</ul>
<p>def regular?<br>
@@ -924,7 +932,7 @@ class Matrix</p>
<a name="Overrides-Objectto_s"></a>
<h1 >Overrides Object#to_s<a href="#Overrides-Objectto_s" class="wiki-anchor">¶</a></h1>
<h1></h1>
<p>def to_s</p>
<ul>
<li>if row_size == 0 || column_size == 0</li>
</ul>
<ul>
<li>if empty?<br>
"Matrix.empty(#{row_size}, #{column_size})"<br>
else<br>
"Matrix[" + @rows.collect{|row|<br>
@@ -939,7 +947,7 @@ class Matrix</li>
</ul>
<a name="Overrides-Objectinspect"></a>
<h1 >Overrides Object#inspect<a href="#Overrides-Objectinspect" class="wiki-anchor">¶</a></h1>
<h1></h1>
<p>def inspect</p>
<ul>
<li>if row_size == 0 || column_size == 0</li>
</ul>
<ul>
<li>if empty?<br>
"Matrix.empty(#{row_size}, #{column_size})"<br>
else<br>
"Matrix#{@rows.inspect}"<br>
=end</li>
</ul> Ruby 1.8 - Backport #2261 (Closed): lib/matrix: following the extensive changeshttps://bugs.ruby-lang.org/issues/22612009-10-24T15:33:53Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
(a) Shouldn't r25412 (and other related commits) be also applied to the 1.8 branch?</p>
<p>(b) r25412 introduced Matrix#inspect_org. It is not documented.</p>
<p>Its output relies on the way the matrix information is stored internally by the Matrix class. Shouldn't this be considered implementation details and thus subject to change and not for public consumption?</p>
<p>It should either:</p>
<ul>
<li>be removed,</li>
<li>be made private (although it is not used by the implementation) or</li>
<li>be documented</li>
</ul>
<p>I recommend the first option</p>
<p>(c) #compare_by_row_vectors and #compare_by should either:</p>
<ul>
<li>be removed (they are not used by the implementation anymore),</li>
<li>be made private or</li>
<li>be documented.</li>
</ul>
<p>I recommend the first option<br>
=end</p> Ruby master - Bug #2224 (Closed): lib/delegate: operator delegationhttps://bugs.ruby-lang.org/issues/22242009-10-17T13:15:20Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
The operators ==, =~, !~ are forwarded by Delegate. I presume the operators != and ~, new in 1.9, have simply been forgotten?</p>
<p>As for eql? and hash, this is probably intentional. I am still curious as to why, since this can yield surprising results (at least to me):</p>
<p>a = "foo"<br>
a1 = SimpleDelegator.new(a)<br>
a2 = a1.dup<br>
h = {}<br>
h[a] = :bar<br>
h[a1] = :bar<br>
h[a2] = :bar<br>
h # ==> {"foo"=>:bar, "foo"=>:bar, "foo"=>:bar}<br>
=end</p> Ruby master - Bug #2223 (Closed): lib/delegate: security model?https://bugs.ruby-lang.org/issues/22232009-10-17T12:39:33Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
Take the following code:</p>
<p>require 'delegate'<br>
cat = "cat"<br>
dog = SimpleDelegator.new("dog")</p>
<p>cat.untrust<br>
dog.untrust</p>
<p>lambda {$SAFE = 4; cat.upcase!}.call # ==> "CAT"<br>
lambda {$SAFE = 4; dog.upcase!}.call # ==> Security Error</p>
<p>Is that expected?</p>
<p>Maybe #trust, #untrust, #taint and #untaint should both call 'super' and forward the call to <strong>getobj</strong>?</p>
<p>I'm even less sure as to what to do with #tainted? and #untrusted? for mixed cases (i.e. when self and <strong>getobj</strong> have different taintedness/trust). Disallow these cases? return "super || <strong>getobj</strong>.tainted?" ?<br>
=end</p> Ruby master - Bug #2222 (Closed): Regex creation error in safe modehttps://bugs.ruby-lang.org/issues/22222009-10-17T11:58:25Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
$ ruby -e '$SAFE=4; /#{}/o'<br>
-e:1:in `': Insecure: can't modify array (SecurityError)</p>
<p>Fairly recent since error not present in ruby 1.9.2dev (2009-08-30 trunk 24705)<br>
=end</p> Ruby master - Bug #2140 (Closed): Bignum#** broken by lib/mathnhttps://bugs.ruby-lang.org/issues/21402009-09-24T09:37:35Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
ruby -r mathn -e 'p (1<<66)**2'<br>
/usr/local/ruby19/lib/ruby/1.9.1/mathn.rb:50:in <code>**': undefined method </code>power!' for 73786976294838206464:Bignum (NoMethodError)<br>
from -e:1:in `'</p>
<p>Fixed in r25067<br>
=end</p> Ruby master - Bug #1532 (Closed): Improved matrix.rb [patch]https://bugs.ruby-lang.org/issues/15322009-05-29T04:18:55Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
The fact that the 'matrix' library is included in Ruby is very helpful.<br>
There are currently some open bugs, and some improvements could make it easier to use.</p>
<p>Propositions:</p>
<ol>
<li>Matrix#trace should raise an ErrDimensionMismatch if the matrix is not square</li>
</ol>
<p>Mathematically speaking, the trace is only defined for square matrices (ref: wikipedia, mathworld)<br>
Currently, Matrix[[1,2]].trace raises a NoMethodError: undefined method `[]' for nil:NilClass<br>
Note that Matrix[[1,2]].transpose.trace returns 1, although in mathematically, trace(M) = trace(transpose(M))</p>
<p>Raising an ErrDimensionMismatch would bring #trace in line with #inverse</p>
<ol start="2">
<li>Matrix creation methods should perform checks and conversion so that values are stored as an Array of Arrays.</li>
</ol>
<p>Currently, no checking is done, so the following will all be constructed without an error or a warning:<br>
Matrix[:hello]<br>
Matrix[nil]<br>
Matrix[4], etc...</p>
<p>Later on, confusing results or strange errors will happen. E.g.:<br>
Matrix[42].transpose # ==> Matrix[[42], [0], [0], [0]]</p>
<p>A TypeError should be raised if the argument is not convertible to an Array of Arrays.</p>
<p>Moreover, non arrays that should be converted using :to_ary, to be consistent<br>
with the builtin library methods that accept array arguments (e.g. Array#transpose)</p>
<p>Finally, conversion from Vector to arrays should be always be performed. Currently,<br>
a = Matrix[[1,2],[3,4]] # => Matrix[[1, 2], [3, 4]]<br>
b = Matrix.rows([a.row(0), a.row(1)]) # => Matrix[Vector[1, 2], Vector[3, 4]]<br>
a == b # => false<br>
It would be more useful, intuitive and mathematically correct if a == b and b.to_s == "Matrix[[1, 2], [3, 4]]"</p>
<p>The same is true for Vector creation methods. For example currently:<br>
Vector.elements(42, false).size # ==> 4<br>
Vector.elements(Vector[1,2,3]) == Vector.elements([1,2,3]) # ==> false<br>
It would be more useful, intuitive and correct if the first example raises an error and the second returns true</p>
<ol start="3">
<li>Matrix creators should enforce that a matrix is rectangular.</li>
</ol>
<p>Currently, a matrix with irregular rows can be created, e.g. x = Matrix[[1,2],[3]].<br>
Mathematically speaking, that is not a matrix.<br>
Basically none of the Matrix methods are of any use for non-rectangular matrices.<br>
Moreover, many strange errors can occur later on. For example, x.inverse will<br>
raise a "NoMethodError: undefined method `*' for nil:NilClass"</p>
<p>It would be helpful to catch these cases at creation time.<br>
Many creation methods don't have to make any extra checks (e.g. Matrix.scalar), and<br>
all methods of Matrix can bypass this extra check when they have created the arrays themselves<br>
(e.g. Matrix#*). There would be a small cost for creation using Matrix.[] and Matrix.rows,<br>
although it is in O(rows) while most other operations are usually in O(rows x columns),<br>
so the performance difference would be minimal.</p>
<ol start="4">
<li>Matrix should deal with empty matrices.</li>
</ol>
<p>Currently, empty matrices like Matrix[] cause problem.<br>
For example Matrix[].determinant raises an error, so does Matrix[] * Matrix[].</p>
<p>Moreover, if h = Matrix[[], []], then currently h.transpose.transpose != h</p>
<p>While an alternative would be to raise and error, the best solution is to handle<br>
empty matrices properly, both 0x0, 0xn and nx0, as does MatLab, GNU Octave, etc...<br>
See doc and references to the literature in:<br>
<a href="http://www.gnu.org/software/octave/doc/interpreter/Empty-Matrices.html" class="external">http://www.gnu.org/software/octave/doc/interpreter/Empty-Matrices.html</a></p>
<ol start="5">
<li>Out of bound indices should be dealt with.</li>
</ol>
<p>a) Matrix#[row,col] should behave in a consisten way if either row or col is out of bounds.<br>
Currently it returns nil vs raises an obscure error (See redmine #1518.)</p>
<p>b) Matrix[[1]].row(2) raises an obscure error, while Matrix[[1]].column(2) returns Vector[nil, nil]</p>
<p>c) In a similar vein, Matrix[[1]].minor(2..2,1..1) currently raises an error but not<br>
Matrix[[1]].minor(1..1,2..2)</p>
<p>Solutions:<br>
a) To be consistent with array lookup using [], Matrix#[] should return nil for out of bounds elements.<br>
A #fetch method could be added, but the fact that matrices normally won't contain nil or false<br>
makes it easy to deal with out of bounds references, e.g. m[r, c] || 0</p>
<p>b) Contrary to nil, it is not easy nor useful to deal with Vector[nil, nil, ...].<br>
#row, and #column could raise an IndexError, but it is more useful and<br>
more coherent with Array#at, etc... to return nil.</p>
<p>c) The same way Matrix#row and #col can be related to Array#at,<br>
Matrix#minor should have similar semantics to Array#slice. If either starting point<br>
is out of bounds, nil is returned. Otherwise a Matrix is returned, although it can<br>
be smaller than what was requested. This is similar to<br>
[:a, :b].slice(3..10) # => nil<br>
[:a, :b].slice(2..10) # => []<br>
[:a, :b].slice(1..10) # => [:b]<br>
Matrix[[1], [2]].minor(0..10, 2..10) # => nil<br>
Matrix[[1], [2]].minor(0..10, 1..10) # => Matrix[[], []]<br>
Matrix[[1], [2]].minor(1..10, 0..10) # => Matrix[[2]]</p>
<ol start="6">
<li>Matrix#collect, Vector#collect, #collect2, #map2 should return enumerators if no block is given</li>
</ol>
<p>This would be more useful and is consistent with Array#each, etc...</p>
<ol start="7">
<li>Matrix#hash should have less collisions</li>
</ol>
<p>Currently, the following matrices have the same hash:<br>
Matrix[]<br>
Matrix[[0,0], [0,0]]<br>
Matrix[[1,0], [0,1]]<br>
Matrix[[42,42], [666,666]]<br>
Matrix[[1,2,3,4], [5,6,1,2], [3,4,5,6]]</p>
<p>Ideally, these should have different hashes, since they are different matrices.</p>
<ol start="8">
<li>Matrix#compare_by_row_vectors, Vector#compare_by and Vector#init_elements should be made private or disappear.</li>
</ol>
<p>As per the documentation, these are not meant to be used.<br>
As such it would be best if they didn't appear in the list of methods.</p>
<p>The attached patch addresses all these issues.</p>
<p>Moreover, it addresses all matrix-related issues I could find on redmine:<br>
<a href="http://redmine.ruby-lang.org/issues/show/1020" class="external">http://redmine.ruby-lang.org/issues/show/1020</a><br>
<a href="http://redmine.ruby-lang.org/issues/show/1515" class="external">http://redmine.ruby-lang.org/issues/show/1515</a><br>
<a href="http://redmine.ruby-lang.org/issues/show/1516" class="external">http://redmine.ruby-lang.org/issues/show/1516</a><br>
<a href="http://redmine.ruby-lang.org/issues/show/1517" class="external">http://redmine.ruby-lang.org/issues/show/1517</a><br>
<a href="http://redmine.ruby-lang.org/issues/show/1518" class="external">http://redmine.ruby-lang.org/issues/show/1518</a><br>
<a href="http://redmine.ruby-lang.org/issues/show/1526" class="external">http://redmine.ruby-lang.org/issues/show/1526</a><br>
<a href="http://redmine.ruby-lang.org/issues/show/1531" class="external">http://redmine.ruby-lang.org/issues/show/1531</a></p>
<p>Also fixed a bug with #determinant and #determinant_e that would raise an error for some matrices<br>
(for instance any square matrix with m[0][0] == 0, e.g. Matrix[[0,1],[2,3]].determinant # => error raised)</p>
<p>Finally, the following methods are performing faster:<br>
Matrix#collect<br>
Matrix#transpose<br>
Matrix#==<br>
Matrix#eql?<br>
Matrix#hash<br>
Vector#collect<br>
Vector#map2</p>
<p>Note that the branch 'runpaint' of rubyspecs has specs to this patch.<br>
=end</p> Ruby master - Bug #1531 (Closed): Matrix#determinant fails on some matriceshttps://bugs.ruby-lang.org/issues/15312009-05-29T04:17:19Zmarcandre (Marc-Andre Lafortune)marcandre-ruby-core@marc-andre.ca
<p>=begin<br>
ruby1.9 -r matrix -e 'Matrix[[0,1],[1,0]].determinant'<br>
/usr/local/ruby191/lib/ruby/1.9.1/matrix.rb:685:in <code>block (2 levels) in determinant': undefined method </code>+' for nil:NilClass (NoMethodError)<br>
from /usr/local/ruby191/lib/ruby/1.9.1/matrix.rb:684:in <code>loop' from /usr/local/ruby191/lib/ruby/1.9.1/matrix.rb:684:in </code>block in determinant'<br>
from /usr/local/ruby191/lib/ruby/1.9.1/matrix.rb:681:in <code>loop' from /usr/local/ruby191/lib/ruby/1.9.1/matrix.rb:681:in </code>determinant'<br>
from -e:1:in `'</p>
<p>Same with determinant_e.</p>
<p>To fix: gsub('ii','i') in matrix.rb<br>
=end</p>