https://bugs.ruby-lang.org/https://bugs.ruby-lang.org/favicon.ico?17113305112021-10-28T14:50:11ZRuby Issue Tracking SystemRuby master - Feature #18276: `Proc#bind_call(obj)` same as `obj.instance_exec(..., &proc_obj)`https://bugs.ruby-lang.org/issues/18276?journal_id=943952021-10-28T14:50:11Zjeremyevans0 (Jeremy Evans)merch-redmine@jeremyevans.net
<ul></ul><p>I'm in favor of adding this method, and would like to see it support the following:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="n">pr</span> <span class="o">=</span> <span class="o">-></span><span class="p">(</span><span class="o">*</span><span class="n">a</span><span class="p">,</span> <span class="o">**</span><span class="n">kw</span><span class="p">,</span> <span class="o">&</span><span class="n">block</span><span class="p">)</span> <span class="k">do</span>
<span class="c1"># ...</span>
<span class="n">block</span><span class="p">.</span><span class="nf">call</span><span class="p">(</span><span class="n">something</span><span class="p">)</span>
<span class="k">end</span>
<span class="n">pr</span><span class="p">.</span><span class="nf">bind_call</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="n">arg</span><span class="p">,</span> <span class="ss">kw: </span><span class="kp">nil</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">something</span><span class="o">|</span>
<span class="c1"># ...</span>
<span class="k">end</span>
</code></pre>
<p>This would allow you to get the equivalent of <code>instance_exec</code>, but with passing a block to the proc being instance execed, which is not currently possible.</p> Ruby master - Feature #18276: `Proc#bind_call(obj)` same as `obj.instance_exec(..., &proc_obj)`https://bugs.ruby-lang.org/issues/18276?journal_id=943972021-10-28T15:53:43Zjhawthorn (John Hawthorn)
<ul></ul><p>In Rails we use <code>obj.instance_exec(&proc_obj)</code> in a few places. One of the downsides <code>instance_exec</code> has is that it creates a singleton class for <code>obj</code>, which isn't friendly to method caches or JITs. <code>Proc#bind_call</code> would be very useful to us if it behaved similarly but did not create a singleton class.</p> Ruby master - Feature #18276: `Proc#bind_call(obj)` same as `obj.instance_exec(..., &proc_obj)`https://bugs.ruby-lang.org/issues/18276?journal_id=944082021-10-29T20:24:13ZEregon (Benoit Daloze)
<ul></ul><p>jhawthorn (John Hawthorn) wrote in <a href="#note-2">#note-2</a>:</p>
<blockquote>
<p>In Rails we use <code>obj.instance_exec(&proc_obj)</code> in a few places. One of the downsides <code>instance_exec</code> has is that it creates a singleton class for <code>obj</code>, which isn't friendly to method caches or JITs. <code>Proc#bind_call</code> would be very useful to us if it behaved similarly but did not create a singleton class.</p>
</blockquote>
<p>I think <code>instance_exec</code> doesn't create a singleton class, only if needed by e.g. <code>Object.new.instance_exec { def foo; end }</code> (at least on TruffleRuby, maybe it depends how eagerly the cref/default definee is computed by the Ruby implementation).<br>
I think the same applies to <code>Proc#bind_call</code> (<code>-> { def foo; end }.bind_call(Object.new)</code>).</p>
<p>I'm not against such a method, but IMHO this alone is not solving <a class="issue tracker-1 status-5 priority-4 priority-default closed" title="Bug: Ractor.make_shareable does not freeze the receiver of a Proc but allows accessing ivars of it (Closed)" href="https://bugs.ruby-lang.org/issues/18243">#18243</a> in a reasonable way.<br>
If an object is made shareable, every object reachable from it should be frozen or shareable, and magically ignoring the Proc's <code>self</code> is conceptually ugly and I believe very confusing for many.<br>
Maybe <code>Ractor.make_shareable(someProc)</code> should return a Proc with a special receiver (e.g., Qundef), which simply can't be called via <code>.call</code> on any Ractor and can only be called via <code>Proc#bind_call</code>.<br>
Then at least that shareable Proc wouldn't refer to any unshared object (which would violate the docs and expected semantics of <code>Ractor.make_shareable</code>)</p> Ruby master - Feature #18276: `Proc#bind_call(obj)` same as `obj.instance_exec(..., &proc_obj)`https://bugs.ruby-lang.org/issues/18276?journal_id=944092021-10-29T20:29:06ZEregon (Benoit Daloze)
<ul></ul><p>I don't like the idea to alter the semantics of Proc methods just for Ractor though (also it costs extra checks on every Proc#call !).<br>
The best and cleanest solution is IMHO to raise for <code>Ractor.make_shareable(someProc)</code> if the Proc <code>self</code> is not shareable.<br>
See <a href="https://bugs.ruby-lang.org/issues/18243#note-5" class="external">https://bugs.ruby-lang.org/issues/18243#note-5</a> for more details on that idea.</p>
<p>Then <code>Proc#bind_call</code> is simply not needed for Ractor.<br>
I don't mind adding <code>Proc#bind_call</code> for other purposes though.<br>
Changing the <code>self</code> is typically best avoided except for some cases in DSLs, as it breaks what methods the block can call in its lexical context.</p> Ruby master - Feature #18276: `Proc#bind_call(obj)` same as `obj.instance_exec(..., &proc_obj)`https://bugs.ruby-lang.org/issues/18276?journal_id=944112021-10-29T20:30:32ZEregon (Benoit Daloze)
<ul><li><strong>Related to</strong> <i><a class="issue tracker-1 status-5 priority-4 priority-default closed" href="/issues/18243">Bug #18243</a>: Ractor.make_shareable does not freeze the receiver of a Proc but allows accessing ivars of it</i> added</li></ul> Ruby master - Feature #18276: `Proc#bind_call(obj)` same as `obj.instance_exec(..., &proc_obj)`https://bugs.ruby-lang.org/issues/18276?journal_id=944232021-11-01T02:44:26ZDan0042 (Daniel DeLorme)
<ul></ul><p>Why not <code>proc.bind(obj).call</code> ? It seems a more "proper" API, more composable. You can bind once and then call multiple times. Maybe <code>Ractor.make_shareable(proc.bind(nil))</code>. I understand the performance benefit of <code>bind_call</code> but in <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: UnboundMethod#apply (Closed)" href="https://bugs.ruby-lang.org/issues/15955">#15955</a>, UnboundMethod#bind_call was introduced as an optimization for hot spots, to be used by "only some fundamental libraries".</p> Ruby master - Feature #18276: `Proc#bind_call(obj)` same as `obj.instance_exec(..., &proc_obj)`https://bugs.ruby-lang.org/issues/18276?journal_id=944672021-11-03T23:54:05ZEregon (Benoit Daloze)
<ul></ul><p>Dan0042 (Daniel DeLorme) wrote in <a href="#note-6">#note-6</a>:</p>
<blockquote>
<p>Why not <code>proc.bind(obj).call</code> ? It seems a more "proper" API, more composable. You can bind once and then call multiple times. Maybe <code>Ractor.make_shareable(proc.bind(nil))</code>. I understand the performance benefit of <code>bind_call</code> but in <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: UnboundMethod#apply (Closed)" href="https://bugs.ruby-lang.org/issues/15955">#15955</a>, UnboundMethod#bind_call was introduced as an optimization for hot spots, to be used by "only some fundamental libraries".</p>
</blockquote>
<p>:+1: I think that's useful. I thought to the name <code>Proc#with_self(nil)</code> in <a class="issue tracker-1 status-5 priority-4 priority-default closed" title="Bug: Ractor.make_shareable does not freeze the receiver of a Proc but allows accessing ivars of it (Closed)" href="https://bugs.ruby-lang.org/issues/18243">#18243</a> but <code>#bind</code> is much better.<br>
<code>Ractor.make_shareable(proc.bind(nil))</code> is a clean solution, I like it.</p>
<p>I think we don't even need <code>Proc#bind_call</code> then, or only as a replacement for <code>instance_exec(&proc)</code>.</p> Ruby master - Feature #18276: `Proc#bind_call(obj)` same as `obj.instance_exec(..., &proc_obj)`https://bugs.ruby-lang.org/issues/18276?journal_id=944742021-11-04T11:39:29Zbyroot (Jean Boussier)byroot@ruby-lang.org
<ul></ul><blockquote>
<p>only as a replacement for instance_exec(&proc).</p>
</blockquote>
<p>Assuming I correctly understand how it would work, then yes it would be great to have it to replace lots of costly <code>instance_exec</code>.</p> Ruby master - Feature #18276: `Proc#bind_call(obj)` same as `obj.instance_exec(..., &proc_obj)`https://bugs.ruby-lang.org/issues/18276?journal_id=944752021-11-04T13:13:03ZEregon (Benoit Daloze)
<ul></ul><p>byroot (Jean Boussier) wrote in <a href="#note-8">#note-8</a>:</p>
<blockquote>
<p>Assuming I correctly understand how it would work, then yes it would be great to have it to replace lots of costly <code>instance_exec</code>.</p>
</blockquote>
<p>I don't think it would change anything performance-wise. The only thing is it's possible to pass a block to the called proc that way (<a href="https://bugs.ruby-lang.org/issues/18276#note-1" class="external">https://bugs.ruby-lang.org/issues/18276#note-1</a>, <a href="https://bugs.ruby-lang.org/issues/18276#note-3" class="external">https://bugs.ruby-lang.org/issues/18276#note-3</a>).</p> Ruby master - Feature #18276: `Proc#bind_call(obj)` same as `obj.instance_exec(..., &proc_obj)`https://bugs.ruby-lang.org/issues/18276?journal_id=944772021-11-04T13:19:32ZEregon (Benoit Daloze)
<ul></ul><p>Ah, maybe Koichi meant that <code>bind_call</code> doesn't change the default definee like <code>instance_exec</code> does?<br>
i.e., does</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">C</span>
<span class="o">-></span> <span class="p">{</span>
<span class="k">def</span> <span class="nf">foo</span>
<span class="k">end</span>
<span class="p">}.</span><span class="nf">bind_call</span><span class="p">(</span><span class="no">Object</span><span class="p">.</span><span class="nf">new</span><span class="p">)</span>
<span class="k">end</span>
</code></pre>
<p>define <code>foo</code> on that object's singleton class, or as an instance method of class C?</p>
<p>I'd assume on that object's singleton class like <code>instance_exec</code>, but I guess it's not the only possibility.</p> Ruby master - Feature #18276: `Proc#bind_call(obj)` same as `obj.instance_exec(..., &proc_obj)`https://bugs.ruby-lang.org/issues/18276?journal_id=945152021-11-08T15:45:49ZDan0042 (Daniel DeLorme)
<ul></ul><p>Eregon (Benoit Daloze) wrote in <a href="#note-10">#note-10</a>:</p>
<blockquote>
<p>I'd assume on that object's singleton class like <code>instance_exec</code>, but I guess it's not the only possibility.</p>
</blockquote>
<p>I would assume the same thing; it would be pretty strange if this defined a <code>foo</code> method on class C.</p>
<p>But it's interesting how instance_eval/instance_exec automatically creates a singleton_class, I wasn't aware of that before.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="nb">p</span> <span class="no">ObjectSpace</span><span class="p">.</span><span class="nf">each_object</span><span class="p">(</span><span class="no">Class</span><span class="p">).</span><span class="nf">count</span> <span class="c1">#=> 363</span>
<span class="no">Object</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">instance_eval</span><span class="p">{</span> <span class="p">}</span>
<span class="nb">p</span> <span class="no">ObjectSpace</span><span class="p">.</span><span class="nf">each_object</span><span class="p">(</span><span class="no">Class</span><span class="p">).</span><span class="nf">count</span> <span class="c1">#=> 364</span>
<span class="no">Object</span><span class="p">.</span><span class="nf">new</span><span class="p">.</span><span class="nf">instance_exec</span><span class="p">{</span> <span class="p">}</span>
<span class="nb">p</span> <span class="no">ObjectSpace</span><span class="p">.</span><span class="nf">each_object</span><span class="p">(</span><span class="no">Class</span><span class="p">).</span><span class="nf">count</span> <span class="c1">#=> 365</span>
</code></pre>
<p>It looks like the singleton_class is eagerly created just in case the eval'd block contains a <code>def</code>. But it shouldn't be too hard to fix that to lazily create the singleton_class when needed.</p> Ruby master - Feature #18276: `Proc#bind_call(obj)` same as `obj.instance_exec(..., &proc_obj)`https://bugs.ruby-lang.org/issues/18276?journal_id=946662021-11-16T06:22:05Zko1 (Koichi Sasada)
<ul></ul><p>Thank you for discussion.</p>
<p>My assumption is not same as <code>instance_exec/eval</code>, only replacing the <code>self</code>.<br>
So the description was wrong.</p> Ruby master - Feature #18276: `Proc#bind_call(obj)` same as `obj.instance_exec(..., &proc_obj)`https://bugs.ruby-lang.org/issues/18276?journal_id=946672021-11-16T06:23:20Zko1 (Koichi Sasada)
<ul></ul><p>Dan0042 (Daniel DeLorme) wrote in <a href="#note-6">#note-6</a>:</p>
<blockquote>
<p>Why not <code>proc.bind(obj).call</code> ? It seems a more "proper" API, more composable. You can bind once and then call multiple times. Maybe <code>Ractor.make_shareable(proc.bind(nil))</code>. I understand the performance benefit of <code>bind_call</code> but in <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: UnboundMethod#apply (Closed)" href="https://bugs.ruby-lang.org/issues/15955">#15955</a>, UnboundMethod#bind_call was introduced as an optimization for hot spots, to be used by "only some fundamental libraries".</p>
</blockquote>
<p><code>Proc#bind(obj)</code> returns new Proc or modify the Proc (mutate the Proc)?</p> Ruby master - Feature #18276: `Proc#bind_call(obj)` same as `obj.instance_exec(..., &proc_obj)`https://bugs.ruby-lang.org/issues/18276?journal_id=946782021-11-16T12:31:54ZEregon (Benoit Daloze)
<ul></ul><p>ko1 (Koichi Sasada) wrote in <a href="#note-13">#note-13</a>:</p>
<blockquote>
<p><code>Proc#bind(obj)</code> returns new Proc or modify the Proc (mutate the Proc)?</p>
</blockquote>
<p>Returns a new Proc, mutation would be very bad (similar to changing from proc to lambda semantics).</p>
<blockquote>
<p>My assumption is not same as instance_exec/eval, only replacing the self.</p>
</blockquote>
<p>I think we should optimize #instance_exec in CRuby so it only creates the singleton class lazily, like on TruffleRuby.<br>
It seems several people care about that.</p>
<p>Not changing the default definee seems confusing, I think .bind.call/.bind_call should behave like instance_exec in that regard (instance_exec can already be surprising, let's not make an extra variant of it with subtle changes).</p> Ruby master - Feature #18276: `Proc#bind_call(obj)` same as `obj.instance_exec(..., &proc_obj)`https://bugs.ruby-lang.org/issues/18276?journal_id=950822021-12-03T04:04:33Zko1 (Koichi Sasada)
<ul><li><strong>Status</strong> changed from <i>Open</i> to <i>Rejected</i></li></ul><p>Ok, I close this ticket.</p>