Ruby Issue Tracking System: Issues
https://bugs.ruby-lang.org/
https://bugs.ruby-lang.org/favicon.ico?1711330511
2021-02-27T17:23:33Z
Ruby Issue Tracking System
Redmine
Ruby master - Feature #17663 (Open): Enumerator#with, an alternative to Enumerator#with_object
https://bugs.ruby-lang.org/issues/17663
2021-02-27T17:23:33Z
RichOrElse (Ritchie Buitre)
<p><strong>Enumerator#with</strong> yields each element along with the arguments</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">Enumerator</span>
<span class="k">def</span> <span class="nf">with</span><span class="p">(</span><span class="o">*</span><span class="n">options</span><span class="p">)</span>
<span class="k">return</span> <span class="n">to_enum</span><span class="p">(</span><span class="ss">:with</span><span class="p">,</span> <span class="o">*</span><span class="n">options</span><span class="p">)</span> <span class="k">unless</span> <span class="k">defined?</span> <span class="k">yield</span>
<span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">entry</span><span class="o">|</span>
<span class="k">yield</span> <span class="n">entry</span><span class="p">,</span> <span class="o">*</span><span class="n">options</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<p>Suppose we have a proc that accepts more than 1 argument.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="nb">format</span> <span class="o">=</span> <span class="nb">proc</span> <span class="k">do</span> <span class="o">|</span><span class="n">value</span><span class="p">,</span> <span class="o">*</span><span class="n">option</span><span class="o">|</span>
<span class="n">value</span><span class="p">.</span><span class="nf">to_s</span><span class="p">(</span><span class="o">*</span><span class="n">option</span><span class="p">)</span>
<span class="k">end</span>
</code></pre>
<p>Normally to apply the argument we enclosed it in a block, like so:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="p">(</span><span class="mi">10</span><span class="o">..</span><span class="mi">15</span><span class="p">).</span><span class="nf">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">n</span><span class="o">|</span> <span class="nb">format</span><span class="o">.</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span> <span class="p">}</span> <span class="c1"># => ["a", "b", "c", "d", "e", "f"]</span>
</code></pre>
<p>I found <strong>Enumerator#with_object</strong> method awkward to use.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="p">(</span><span class="mi">10</span><span class="o">..</span><span class="mi">15</span><span class="p">).</span><span class="nf">each</span><span class="p">.</span><span class="nf">with_object</span><span class="p">(</span><span class="mi">16</span><span class="p">).</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="nb">format</span><span class="p">)</span> <span class="c1"># => ["a", "b", "c", "d", "e", "f"]</span>
</code></pre>
<p>Tried simplifying this code further, but <strong>Enumerator#with_object</strong> ignores the given block and just returns the argument.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="p">(</span><span class="mi">10</span><span class="o">..</span><span class="mi">15</span><span class="p">).</span><span class="nf">map</span><span class="p">.</span><span class="nf">with_object</span><span class="p">(</span><span class="mi">16</span><span class="p">,</span> <span class="o">&</span><span class="nb">format</span><span class="p">)</span> <span class="c1"># => 16</span>
</code></pre>
<p>Compare to how concise this line using <strong>Enumerator#with</strong></p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="p">(</span><span class="mi">10</span><span class="o">..</span><span class="mi">15</span><span class="p">).</span><span class="nf">map</span><span class="p">.</span><span class="nf">with</span><span class="p">(</span><span class="mi">16</span><span class="p">,</span> <span class="o">&</span><span class="nb">format</span><span class="p">)</span> <span class="c1"># => ["a", "b", "c", "d", "e", "f"]</span>
</code></pre>
Ruby master - Feature #15302 (Open): Proc#with and Proc#by, for partial function application and ...
https://bugs.ruby-lang.org/issues/15302
2018-11-14T06:56:34Z
RichOrElse (Ritchie Buitre)
<p><strong>Proc#by</strong> allows currying implicitly</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">Proc</span>
<span class="k">def</span> <span class="nf">by</span><span class="p">(</span><span class="o">*</span><span class="n">head</span><span class="p">)</span>
<span class="k">return</span> <span class="nb">self</span> <span class="k">if</span> <span class="n">head</span><span class="p">.</span><span class="nf">none?</span>
<span class="n">curry</span><span class="p">(</span><span class="n">head</span><span class="p">.</span><span class="nf">size</span><span class="p">.</span><span class="nf">next</span><span class="p">)</span><span class="o">.</span><span class="p">(</span><span class="o">*</span><span class="n">head</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">class</span> <span class="nc">Method</span>
<span class="k">def</span> <span class="nf">by</span><span class="p">(</span><span class="o">*</span><span class="n">head</span><span class="p">)</span>
<span class="n">to_proc</span><span class="p">.</span><span class="nf">by</span><span class="p">(</span><span class="o">*</span><span class="n">head</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">class</span> <span class="nc">Symbol</span>
<span class="k">def</span> <span class="nf">by</span><span class="p">(</span><span class="o">*</span><span class="n">head</span><span class="p">)</span>
<span class="n">to_proc</span><span class="p">.</span><span class="nf">by</span><span class="p">(</span><span class="o">*</span><span class="n">head</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">double</span> <span class="o">=</span> <span class="p">:</span><span class="o">*</span><span class="p">.</span><span class="nf">by</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="c1"># => proc { |n| 2 * n }</span>
</code></pre>
<p><strong>Proc#with</strong> pre-defines trailing arguments and/or block.</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="k">class</span> <span class="nc">Proc</span>
<span class="k">def</span> <span class="nf">with</span><span class="p">(</span><span class="o">*</span><span class="n">tail</span><span class="p">,</span> <span class="o">&</span><span class="n">blk</span><span class="p">)</span>
<span class="k">if</span> <span class="n">arity</span> <span class="o">==</span> <span class="n">tail</span><span class="p">.</span><span class="nf">size</span><span class="p">.</span><span class="nf">next</span>
<span class="nb">proc</span> <span class="p">{</span> <span class="o">|</span><span class="n">head</span><span class="o">|</span> <span class="n">call</span> <span class="n">head</span><span class="p">,</span> <span class="o">*</span><span class="n">tail</span><span class="p">,</span> <span class="o">&</span><span class="n">blk</span> <span class="p">}</span>
<span class="k">else</span>
<span class="nb">proc</span> <span class="p">{</span> <span class="o">|*</span><span class="n">head</span><span class="o">|</span> <span class="n">call</span> <span class="o">*</span><span class="n">head</span><span class="p">,</span> <span class="o">*</span><span class="n">tail</span><span class="p">,</span> <span class="o">&</span><span class="n">blk</span> <span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">class</span> <span class="nc">Method</span>
<span class="k">def</span> <span class="nf">with</span><span class="p">(</span><span class="o">*</span><span class="n">head</span><span class="p">,</span> <span class="o">&</span><span class="n">blk</span><span class="p">)</span>
<span class="n">to_proc</span><span class="p">.</span><span class="nf">with</span><span class="p">(</span><span class="o">*</span><span class="n">head</span><span class="p">,</span> <span class="o">&</span><span class="n">blk</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">class</span> <span class="nc">Symbol</span>
<span class="k">def</span> <span class="nf">with</span><span class="p">(</span><span class="o">*</span><span class="n">head</span><span class="p">,</span> <span class="o">&</span><span class="n">blk</span><span class="p">)</span>
<span class="n">to_proc</span><span class="p">.</span><span class="nf">with</span><span class="p">(</span><span class="o">*</span><span class="n">head</span><span class="p">,</span> <span class="o">&</span><span class="n">blk</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">double</span> <span class="o">=</span> <span class="p">:</span><span class="o">*</span><span class="p">.</span><span class="nf">with</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="c1"># => proc { |n| n * 2 }</span>
</code></pre>
<p>That's the basic idea, but I've also expanded on it by optimising and defining operators (+, &, |) and other methods (Proc#such) <a href="https://gist.github.com/RichOrElse/12d056be5757ec7ce540708bbac2b584" class="external">here</a>.</p>