https://redmine.ruby-lang.org/https://redmine.ruby-lang.org/favicon.ico?17113305112018-05-22T21:24:12ZRuby Issue Tracking SystemRuby master - Feature #14781: Enumerator.generatehttps://redmine.ruby-lang.org/issues/14781?journal_id=722152018-05-22T21:24:12Zshevegen (Robert A. Heiler)shevegen@gmail.com
<ul></ul><p>I agree with the proposal and name.</p>
<p>I would like to recommend and suggest you to add it to the next ruby<br>
developer meeting for matz' to have a look at and decide. (I think most<br>
people already commented on the other linked suggestion, so I assume<br>
that the issue here will remain fairly small.)</p>
<p>By the way props on the examples given; it's a very clean proposal,<br>
much cleaner than any proposals I ever did myself :D, and it can be<br>
taken almost as-is for the official documentation, IMO. :)</p>
<p>Now of course we have to wait and see what matz and the other core<br>
devs have to say about it.</p>
<p>Here is the link to the latest developer meeting:</p>
<p><a href="https://bugs.ruby-lang.org/issues/14769" class="external">https://bugs.ruby-lang.org/issues/14769</a></p> Ruby master - Feature #14781: Enumerator.generatehttps://redmine.ruby-lang.org/issues/14781?journal_id=725612018-06-21T08:02:49Zknu (Akinori MUSHA)knu@ruby-lang.org
<ul></ul><p>What about adding support for ending an iteration from a given block itself by raising StopIteration, rather than having to chain it with take_while?</p> Ruby master - Feature #14781: Enumerator.generatehttps://redmine.ruby-lang.org/issues/14781?journal_id=725632018-06-21T08:17:44Zknu (Akinori MUSHA)knu@ruby-lang.org
<ul></ul><p>In today's developer meeting, we kind of loved the functionality, but haven't reached a conclusion about the name.</p>
<p>Some candidates:</p>
<ul>
<li>
<p>Enumerator.iterate(initial = nil) { |x| ... }<br>
Haskell has a similar function named iterate.</p>
</li>
<li>
<p>Enumerator.from(initial) { |x| ... }<br>
This would sound natural when the initial value is mandatory.</p>
</li>
</ul> Ruby master - Feature #14781: Enumerator.generatehttps://redmine.ruby-lang.org/issues/14781?journal_id=725642018-06-21T08:26:39Zmatz (Yukihiro Matsumoto)matz@ruby.or.jp
<ul><li><strong>Status</strong> changed from <i>Open</i> to <i>Feedback</i></li></ul><p>I am not fully satisfied with the name <code>generate</code> since the word does not always imply sequence generation. If someone has better name proposal, I welcome.</p>
<p>Matz.</p> Ruby master - Feature #14781: Enumerator.generatehttps://redmine.ruby-lang.org/issues/14781?journal_id=725672018-06-21T08:59:47Zsawa (Tsuyoshi Sawada)
<ul></ul><p>I propose the following:</p>
<ul>
<li>Enumerator.sequence</li>
<li>Enumerator.recur</li>
</ul> Ruby master - Feature #14781: Enumerator.generatehttps://redmine.ruby-lang.org/issues/14781?journal_id=725712018-06-21T10:06:16Zzverok (Victor Shepelev)zverok.offline@gmail.com
<ul></ul><p>I like <code>#sequence</code>, too.</p> Ruby master - Feature #14781: Enumerator.generatehttps://redmine.ruby-lang.org/issues/14781?journal_id=725722018-06-21T10:07:44Zzverok (Victor Shepelev)zverok.offline@gmail.com
<ul></ul><p>Though, I should add that <code>Enumerator.generate</code> (seen this way, not just <code>.generate</code> alone) seems to clearly state "generate enumerator" :)</p> Ruby master - Feature #14781: Enumerator.generatehttps://redmine.ruby-lang.org/issues/14781?journal_id=725732018-06-21T10:21:28Zmame (Yusuke Endoh)mame@ruby-lang.org
<ul></ul><p>zverok (Victor Shepelev) wrote:</p>
<blockquote>
<p>Though, I should add that <code>Enumerator.generate</code> (seen this way, not just <code>.generate</code> alone) seems to clearly state "generate enumerator" :)</p>
</blockquote>
<p>"generate" seems too general. It looks the most typical or primitive way to create an enumerator, but it is not.</p>
<p>Haskell provides "iterate" function for this feature, but it resembles an iterator in Ruby.</p> Ruby master - Feature #14781: Enumerator.generatehttps://redmine.ruby-lang.org/issues/14781?journal_id=725752018-06-21T11:14:53ZEregon (Benoit Daloze)
<ul></ul><p>I like Enumerator.generate, since it's really to generate a lazy sequence, to generate an Enumerator, from a block.</p>
<p>In the end it is basically as powerful as Enumerator.new, so I see no problem to have a factory/constructor-like name.</p>
<p>Enumerator.sequence sounds like it could be an eager sequence, and doesn't tell me the block is generating the next value, so I don't like it.</p>
<p>Enumerator.iterate sounds like we would iterate something, but we don't, we generate a sequence lazily.<br>
The iteration itself is done by #each, not "Enumerator.iterate".<br>
I think it only works well in Haskell due to their first-class functions, but even then "iterating" (repeated applications of) a function doesn't sound clear to me or map well to Ruby.</p> Ruby master - Feature #14781: Enumerator.generatehttps://redmine.ruby-lang.org/issues/14781?journal_id=725802018-06-21T13:49:47Zmatz (Yukihiro Matsumoto)matz@ruby.or.jp
<ul></ul><p>I don't like <code>recur</code>. Probably it came from <code>recurrence</code> but programmers usually think of <code>recursive</code> because they see <code>recursive</code> more often. FYI, the word <code>recur</code> is used in Clojure for the recursive purpose. I don't like <code>iterate</code> either, as <a class="user active user-mention" href="https://redmine.ruby-lang.org/users/772">@Eregon (Benoit Daloze)</a> stated.</p>
<p><code>Enumerator.generate</code> may work because it <strong>generates Enumerator</strong> in a fashion different from <code>Enumerator.new</code>.</p>
<p>Matz.</p> Ruby master - Feature #14781: Enumerator.generatehttps://redmine.ruby-lang.org/issues/14781?journal_id=725812018-06-21T14:04:17Zmame (Yusuke Endoh)mame@ruby-lang.org
<ul></ul><p>Ah, I meant <code>iterate</code> is not a good name for ruby. Sorry for the confusion.</p> Ruby master - Feature #14781: Enumerator.generatehttps://redmine.ruby-lang.org/issues/14781?journal_id=725952018-06-22T02:35:07Zknu (Akinori MUSHA)knu@ruby-lang.org
<ul></ul><p>I'm not very fond of <code>generate</code> because it's not the only way to generate an Enumerator. There could be more to come.</p> Ruby master - Feature #14781: Enumerator.generatehttps://redmine.ruby-lang.org/issues/14781?journal_id=743372018-10-07T12:26:24Zzverok (Victor Shepelev)zverok.offline@gmail.com
<ul><li><strong>Description</strong> updated (<a title="View differences" href="/journals/74337/diff?detail_id=50030">diff</a>)</li></ul> Ruby master - Feature #14781: Enumerator.generatehttps://redmine.ruby-lang.org/issues/14781?journal_id=743862018-10-10T06:44:28Zakr (Akira Tanaka)akr@fsij.org
<ul></ul><p>How about "recurrence" as method name?<br>
It is noun, though.</p> Ruby master - Feature #14781: Enumerator.generatehttps://redmine.ruby-lang.org/issues/14781?journal_id=744982018-10-18T14:24:49Zknu (Akinori MUSHA)knu@ruby-lang.org
<ul><li><strong>File</strong> <a href="/attachments/7422">enumerator_from.rb</a> <a class="icon-only icon-download" title="Download" href="/attachments/download/7422/enumerator_from.rb">enumerator_from.rb</a> added</li></ul><p>I've been thinking about this, and I have some ideas I want to share:</p>
<ul>
<li>To recursively traverse ancestors of a node is one of the most typical use cases, so that should be made easy to do.</li>
<li>When and how to end a sequence may vary, so there should be some flexibility in defining an end. For example, nil is not always the dead end. It could mean something; you might even want to end a sequence with an explicit nil as sentinel. Rescuing an exception and treating it as an end might not be a good option because that would make debugging hard, but StopIteration should be a good fit as a signal for an end.</li>
<li>Sometimes you'd need to look back two or more steps to generate a new value (not to mention Fibonacci series), so the constructor should preferably take multiple seeds.</li>
<li>Sometimes seeds are not subject of yielding; it would be handy if you could specify how many leading seeds to skip.</li>
</ul>
<p>In the original proposal, there are some tricks needed to define an end of a sequence or to look back multiple preceding terms, so I've come up with an alternative API that builds them in as keyword options:</p>
<pre><code>Enumerator.from(seeds, drop: 0, allow_nil: false) { |*preceding_terms| next_term }
seeds: Array of objects to be used as seeds (required, but can be an empty array)
drop: How many leading terms to skip
allow_nil: True if nil should not end the enumerator
</code></pre>
<p>I wrote an experimental implementation and test cases in the attached file.</p>
<p>I'll be working on it further in this weekends' hackathon, so any input is appreciated!</p> Ruby master - Feature #14781: Enumerator.generatehttps://redmine.ruby-lang.org/issues/14781?journal_id=745002018-10-18T18:30:28Zzverok (Victor Shepelev)zverok.offline@gmail.com
<ul></ul><p><a class="user active user-mention" href="https://redmine.ruby-lang.org/users/8">@knu (Akinori MUSHA)</a><br>
The <em>ultimate</em> goal for my proposal is, in fact, promoting Enumerator as a "Ruby way" for doing all-the-things with loops; not just "new useful feature".</p>
<p>That's why I feel really uneasy about your changes to the proposal.</p>
<p><strong>drop</strong></p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># from: `drop: 2` is part of Enumerator.from API</span>
<span class="no">Enumerator</span><span class="p">.</span><span class="nf">from</span><span class="p">([</span><span class="n">node</span><span class="p">],</span> <span class="ss">drop: </span><span class="mi">2</span><span class="p">,</span> <span class="o">&</span><span class="ss">:parent</span><span class="p">).</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="ss">:name</span><span class="p">)</span>
<span class="c1"># generate: `drop(2)` is part of standard Enumerator API</span>
<span class="no">Enumerator</span><span class="p">.</span><span class="nf">generate</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="o">&</span><span class="ss">:parent</span><span class="p">).</span><span class="nf">take</span><span class="p">(</span><span class="mi">6</span><span class="p">).</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="ss">:name</span><span class="p">).</span><span class="nf">drop</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
</code></pre>
<p><strong>allow_nil</strong> (by default <code>false</code>: <code>nil</code> stops enumeration)</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># from:</span>
<span class="c1"># implicit "stop on nil" is part of Enumerator.from convention that code reader should be aware of</span>
<span class="no">Enumerator</span><span class="p">.</span><span class="nf">from</span><span class="p">([</span><span class="n">node</span><span class="p">],</span> <span class="o">&</span><span class="ss">:parent</span><span class="p">).</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="ss">:name</span><span class="p">)</span>
<span class="c1"># don't stop on nil is explicit part of the API</span>
<span class="no">Enumerator</span><span class="p">.</span><span class="nf">from</span><span class="p">([</span><span class="n">node</span><span class="p">],</span> <span class="ss">allow_nil: </span><span class="kp">true</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">n</span><span class="o">|</span>
<span class="k">raise</span> <span class="no">StopIteration</span> <span class="k">if</span> <span class="n">n</span><span class="p">.</span><span class="nf">nil?</span>
<span class="n">n</span><span class="p">.</span><span class="nf">parent</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="n">n</span><span class="o">&</span><span class="p">.</span><span class="nf">name</span> <span class="p">}</span>
<span class="c1"># generate: "stop on nil" is explicit and obvious</span>
<span class="no">Enumerator</span><span class="p">.</span><span class="nf">generate</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="o">&</span><span class="ss">:parent</span><span class="p">).</span><span class="nf">take_while</span><span class="p">(</span><span class="o">&</span><span class="ss">:itself</span><span class="p">).</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="ss">:name</span><span class="p">)</span>
<span class="c1"># no mentioning of unnecessary "we don't need to stop on nil", no additional thinking</span>
<span class="nb">p</span> <span class="no">Enumerator</span><span class="p">.</span><span class="nf">generate</span><span class="p">(</span><span class="n">node</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">n</span><span class="o">|</span>
<span class="k">raise</span> <span class="no">StopIteration</span> <span class="k">if</span> <span class="n">n</span><span class="p">.</span><span class="nf">nil?</span>
<span class="n">n</span><span class="p">.</span><span class="nf">parent</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="n">n</span><span class="o">&</span><span class="p">.</span><span class="nf">name</span> <span class="p">}</span>
</code></pre>
<p><strong>start with array</strong> (I believe 1 and 0 initial values are the MOST used cases)</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># from: we should start from empty array, expression nothing but Enumerator.from API limitation</span>
<span class="no">Enumerator</span><span class="p">.</span><span class="nf">from</span><span class="p">([])</span> <span class="p">{</span> <span class="mi">0</span> <span class="p">}.</span><span class="nf">take</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
<span class="c1"># generate: no start value</span>
<span class="no">Enumerator</span><span class="p">.</span><span class="nf">generate</span> <span class="p">{</span> <span class="mi">0</span> <span class="p">}.</span><span class="nf">take</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
<span class="c1"># from: work with one value requires not forgetting to arrayify it </span>
<span class="no">Enumerator</span><span class="p">.</span><span class="nf">from</span><span class="p">([</span><span class="mi">1</span><span class="p">],</span> <span class="o">&</span><span class="ss">:succ</span><span class="p">).</span><span class="nf">take</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
<span class="c1"># generate: just use the value</span>
<span class="no">Enumerator</span><span class="p">.</span><span class="nf">generate</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="o">&</span><span class="ss">:succ</span><span class="p">).</span><span class="nf">take</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
<span class="c1"># from: "we pass as much of previous values as initial array had" convention</span>
<span class="no">Enumerator</span><span class="p">.</span><span class="nf">from</span><span class="p">([</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">])</span> <span class="p">{</span> <span class="o">|</span><span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="o">|</span> <span class="n">i</span> <span class="o">+</span> <span class="n">j</span> <span class="p">}.</span><span class="nf">take</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
<span class="c1"># generate: regular value enumeration, next block receives exactly what previous returns</span>
<span class="no">Enumerator</span><span class="p">.</span><span class="nf">generate</span><span class="p">([</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">])</span> <span class="p">{</span> <span class="o">|</span><span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="o">|</span> <span class="p">[</span><span class="n">j</span><span class="p">,</span> <span class="n">i</span> <span class="o">+</span> <span class="n">j</span><span class="p">]</span> <span class="p">}.</span><span class="nf">take</span><span class="p">(</span><span class="mi">10</span><span class="p">).</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="ss">:last</span><span class="p">)</span>
<span class="c1"># ^ yes, it will require additional trick to include 0 in final result, but I believe this is worthy sacrifice</span>
</code></pre>
<p>The problem with "API complication" is inconsistency. Like, a newcomer may ask: Why <code>Enumerator.from</code> has "this handy <code>drop: 2</code> initial arg", and <code>each</code> don't? Use cases could exist, too!</p> Ruby master - Feature #14781: Enumerator.generatehttps://redmine.ruby-lang.org/issues/14781?journal_id=745062018-10-19T03:30:05Zknu (Akinori MUSHA)knu@ruby-lang.org
<ul></ul><p>zverok (Victor Shepelev) wrote:</p>
<blockquote>
<p><a class="user active user-mention" href="https://redmine.ruby-lang.org/users/8">@knu (Akinori MUSHA)</a><br>
The <em>ultimate</em> goal for my proposal is, in fact, promoting Enumerator as a "Ruby way" for doing all-the-things with loops; not just "new useful feature".</p>
<p>That's why I feel really uneasy about your changes to the proposal.</p>
</blockquote>
<p>Thanks for your quick feedback, and for bringing up this issue.</p>
<blockquote>
<p><strong>drop</strong></p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># from: `drop: 2` is part of Enumerator.from API</span>
<span class="no">Enumerator</span><span class="p">.</span><span class="nf">from</span><span class="p">([</span><span class="n">node</span><span class="p">],</span> <span class="ss">drop: </span><span class="mi">2</span><span class="p">,</span> <span class="o">&</span><span class="ss">:parent</span><span class="p">).</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="ss">:name</span><span class="p">)</span>
<span class="c1"># generate: `drop(2)` is part of standard Enumerator API</span>
<span class="no">Enumerator</span><span class="p">.</span><span class="nf">generate</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="o">&</span><span class="ss">:parent</span><span class="p">).</span><span class="nf">take</span><span class="p">(</span><span class="mi">6</span><span class="p">).</span><span class="nf">map</span><span class="p">(</span><span class="o">&</span><span class="ss">:name</span><span class="p">).</span><span class="nf">drop</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
</code></pre>
</blockquote>
<p>I presume <code>.take(6)</code> is inserted by mistake, but with it or not the following map and drop methods belong to Enumerable, and are Array based operations that create an intermediate array per call. So, I consider them as Array/Enumerable API rather than Enumerator API. Creating intermediate arrays is not only a waste of memory but also against the key concept of Enumerator: to deal with an object as a stream, which may be infinite.</p>
<p>Adding <code>.lazy</code> before <code>.drop(2)</code> can be a cure, but then the value you get is a lazy enumerator that is incompatible with an non-lazy enumerator. For instance, Lazy#map, Lazy#select etc. return Lazy objects, so you can't always pass one to methods that expect a normal Enumerable object.</p>
<p>I've always thought that Lazy#eager that turns a lazy enumerator back to a non-lazy enumerator would be nice, but <code>.lazy.map{}.eager</code> would look messy anyway.</p>
<blockquote>
<pre><code># implicit "stop on nil" is part of Enumerator.from convention that code reader should be aware of
</code></pre>
</blockquote>
<p>I think it's good and reasonable default behavior to treat nil as an end. Taking your Octokit example, the block could be <code>{ |response| response.rels[:next]&.get }</code> to make it go through all pages and automatically stop if nil were treated as an end. You omitted a <code>.take_while</code> in the example, but you'd get an error if there were less than 3 pages. You'd almost always need to either explicitly raise StopIteration in the initial block or chain <code>.take_while</code>/<code>.take</code> if there were no default end, and the choice between them is not obvious.</p>
<blockquote>
<p><strong>start with array</strong> (I believe 1 and 0 initial values are the MOST used cases)</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="c1"># from: we should start from empty array, expression nothing but Enumerator.from API limitation</span>
<span class="no">Enumerator</span><span class="p">.</span><span class="nf">from</span><span class="p">([])</span> <span class="p">{</span> <span class="mi">0</span> <span class="p">}.</span><span class="nf">take</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
<span class="c1"># generate: no start value</span>
<span class="no">Enumerator</span><span class="p">.</span><span class="nf">generate</span> <span class="p">{</span> <span class="mi">0</span> <span class="p">}.</span><span class="nf">take</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
</code></pre>
</blockquote>
<p>The limitation only came from what the word <code>from</code> sounds like. I picked the name <code>from</code> and <code>Enumerator.from {}</code> just didn't sound right to me, so I made the argument mandatory. You can just default the first argument to <code>[]</code> if it reads and writes better, possibly with a different name than <code>from</code> which I won't insist on.</p>
<blockquote>
<pre><code># from: work with one value requires not forgetting to arrayify it
Enumerator.from([1], &:succ).take(10)
# generate: just use the value
Enumerator.generate(1, &:succ).take(10)
</code></pre>
</blockquote>
<p>Yeah, due to our keyword arguments being pseudo ones, you can't use variable length arguments for a list of objects that might end with a hash. We'll hopefully be getting it right by Ruby 3.0.</p>
<p>There's much room for consideration of the name and method signature. Perhaps multiple factory methods could work better.</p>
<blockquote>
<pre><code># from: "we pass as much of previous values as initial array had" convention
Enumerator.from([0, 1]) { |i, j| i + j }.take(10)
# generate: regular value enumeration, next block receives exactly what previous returns
Enumerator.generate([0, 1]) { |i, j| [j, i + j] }.take(10).map(&:last)
# ^ yes, it will require additional trick to include 0 in final result, but I believe this is worthy sacrifice
</code></pre>
</blockquote>
<p>The former directly generates an infinite Fibonacci sequence and that's a major difference. Taking a first few elements with <code>.take</code> is just for testing (assertion) purposes and not part of the use case. When solving a problem like "Find the least n such that \sum_{k=1}^{n} fib(k) >= 1000", <code>take</code> wouldn't work optimally.</p>
<blockquote>
<p>The problem with "API complication" is inconsistency. Like, a newcomer may ask: Why <code>Enumerator.from</code> has "this handy <code>drop: 2</code> initial arg", and <code>each</code> don't? Use cases could exist, too!</p>
</blockquote>
<p>I understand that sentiment, but there's no surprise that a factory/constructor method of a dedicated class often takes many tunables while individual instance methods do not. If people all said they need it as a generic feature, it wouldn't be a bad idea to me to consider adding something like Enumerable#skip(n) that would return an offset enumerator.</p> Ruby master - Feature #14781: Enumerator.generatehttps://redmine.ruby-lang.org/issues/14781?journal_id=772562019-03-22T07:25:40Zknu (Akinori MUSHA)knu@ruby-lang.org
<ul><li><strong>Subject</strong> changed from <i>Enumerator#generate</i> to <i>Enumerator.generate</i></li></ul> Ruby master - Feature #14781: Enumerator.generatehttps://redmine.ruby-lang.org/issues/14781?journal_id=805962019-08-11T10:30:44Zzverok (Victor Shepelev)zverok.offline@gmail.com
<ul><li><strong>File</strong> <a href="/attachments/7984">enumerator-generate.patch</a> <a class="icon-only icon-download" title="Download" href="/attachments/download/7984/enumerator-generate.patch">enumerator-generate.patch</a> added</li></ul><p>Attached is a patch with the implementation I initially described, and with the name Matz seems to be accepting of (<code>Enumerator.generate</code>).<br>
I am not 100% sure about the code (as it is my first contribution to core C code), but at least it is short and clean, I am willing to improve it if necessary.</p> Ruby master - Feature #14781: Enumerator.generatehttps://redmine.ruby-lang.org/issues/14781?journal_id=806162019-08-11T20:06:59Zjwmittag (Jörg W Mittag)Ruby-Lang@JoergWMittag.De
<ul></ul><p>zverok (Victor Shepelev) wrote:</p>
<blockquote>
<p>This is alternative proposal to <code>Object#enumerate</code> (<a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Enumerator from single object (Closed)" href="https://redmine.ruby-lang.org/issues/14423">#14423</a>), which was considered by many as a good idea, but with unsure naming and too radical (<code>Object</code> extension). This one is <em>less</em> radical, and, at the same time, more powerful.</p>
<p><strong>Synopsys</strong>:</p>
<ul>
<li>
<code>Enumerator.generate(initial, &block)</code>: produces infinite sequence where each next element is calculated by applying block to previous; <code>initial</code> is first sequence element;</li>
<li>
<code>Enumerator.generate(&block)</code>: the same; first element of sequence is a result of calling the block with no args.</li>
</ul>
</blockquote>
<p>This is typically called <code>unfold</code>, since it is the <a href="https://wikipedia.org/Unfold_(higher-order_function)" class="external">category-theoretical dual of a <code>fold</code></a>. Ruby doesn't use the name <code>fold</code>, though, it uses <code>inject</code> and <code>reduce</code>, neither of which lend themselves to negating: <code>Enumerator::uninject</code>, <code>Enumerator::unreduce</code>? Yikes! That sounds really bad.</p>
<p>However, literally as I am writing this, a thought pops into my mind: how about <code>Enumerator::produce</code> as the dual to <code>Enumerable#reduce</code>? Scala also has <a href="https://scala-lang.org/api/current/scala/collection/immutable/Stream%24.html#iterate%5BA%5D%28start%3AA%29%28f%3AA%3D%3EA%29%3Ascala.collection.immutable.Stream%5BA%5D" class="external"><code>iterate</code></a> which is the restricted variant of <a href="https://scala-lang.org/api/current/scala/collection/immutable/Stream%24.html#unfold%5BA%2CS%5D%28init%3AS%29%28f%3AS%3D%3EOption%5B%28A%2CS%29%5D%29%3ACC%5BA%5D" class="external"><code>unfold</code></a>.</p> Ruby master - Feature #14781: Enumerator.generatehttps://redmine.ruby-lang.org/issues/14781?journal_id=806192019-08-11T20:34:50Zzverok (Victor Shepelev)zverok.offline@gmail.com
<ul></ul><blockquote>
<p>However, literally as I am writing this, a thought pops into my mind: how about Enumerator::produce as the dual to Enumerable#reduce? Scala also has iterate which is the restricted variant of unfold.</p>
</blockquote>
<p>In the <a href="https://bugs.ruby-lang.org/issues/14423" class="external">first</a> proposal, I've studied some alternative names and their logic, including "like <code>iterate</code>" (that was my original proposal: <code>Object#enumerate</code>, but the <code>Enumerator.enumerate</code> seems a bit overkill to me) and "antonym to <code>fold</code> (<code>reduce</code>)" -- this led me to <code>deduce</code>, but your <code>produce</code> idea sounds about right!</p>
<p>Matz?..</p> Ruby master - Feature #14781: Enumerator.generatehttps://redmine.ruby-lang.org/issues/14781?journal_id=806212019-08-11T21:10:57Zjwmittag (Jörg W Mittag)Ruby-Lang@JoergWMittag.De
<ul></ul><p>zverok (Victor Shepelev) wrote:</p>
<blockquote>
<blockquote>
<p>However, literally as I am writing this, a thought pops into my mind: how about Enumerator::produce as the dual to Enumerable#reduce? Scala also has iterate which is the restricted variant of unfold.<br>
In the <a href="https://bugs.ruby-lang.org/issues/14423" class="external">first</a> proposal, I've studied some alternative names and their logic, including "like <code>iterate</code>" (that was my original proposal: <code>Object#enumerate</code>, but the <code>Enumerator.enumerate</code> seems a bit overkill to me) and "antonym to <code>fold</code> (<code>reduce</code>)" -- this led me to <code>deduce</code>, but your <code>produce</code> idea sounds about right!</p>
</blockquote>
</blockquote>
<p>I just realized that I already commented on your first proposal: <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Enumerator from single object (Closed)" href="https://redmine.ruby-lang.org/issues/14423#note-16">#14423-16</a> but didn't remember it :-D</p> Ruby master - Feature #14781: Enumerator.generatehttps://redmine.ruby-lang.org/issues/14781?journal_id=812532019-08-29T06:55:33Zmatz (Yukihiro Matsumoto)matz@ruby.or.jp
<ul></ul><p>I prefer <code>produce</code> to <code>iterate</code>, <code>generate</code> or <code>from</code>. Accepted (at least for the experiment).</p>
<p>Matz.</p> Ruby master - Feature #14781: Enumerator.generatehttps://redmine.ruby-lang.org/issues/14781?journal_id=812672019-08-29T11:10:20Zknu (Akinori MUSHA)knu@ruby-lang.org
<ul><li><strong>File</strong> <a href="/attachments/8030">0001-Implement-Enumerator.produce-Feature-14781.patch</a> <a class="icon-only icon-download" title="Download" href="/attachments/download/8030/0001-Implement-Enumerator.produce-Feature-14781.patch">0001-Implement-Enumerator.produce-Feature-14781.patch</a> added</li></ul><p>I just wrote a draft implementation of Enumerator.produce. I just didn't notice <a class="user active user-mention" href="https://redmine.ruby-lang.org/users/710">@zverok (Victor Shepelev)</a> kindly worked on writing a patch, so I'll review and integrate it later!</p> Ruby master - Feature #14781: Enumerator.generatehttps://redmine.ruby-lang.org/issues/14781?journal_id=815362019-09-12T11:19:42ZAnonymous
<ul><li><strong>Status</strong> changed from <i>Feedback</i> to <i>Closed</i></li></ul><p>Applied in changeset <a class="changeset" title="Implement Enumerator.produce [Feature #14781]" href="https://redmine.ruby-lang.org/projects/ruby-master/repository/git/revisions/775037613bffe6f90e7af510b7f46a2ac10610be">git|775037613bffe6f90e7af510b7f46a2ac10610be</a>.</p>
<hr>
<p>Implement Enumerator.produce [Feature <a class="issue tracker-2 status-5 priority-4 priority-default closed" title="Feature: Enumerator.generate (Closed)" href="https://redmine.ruby-lang.org/issues/14781">#14781</a>]</p> Ruby master - Feature #14781: Enumerator.generatehttps://redmine.ruby-lang.org/issues/14781?journal_id=884892020-11-14T20:55:00Zchrisseaton (Chris Seaton)chris@chrisseaton.com
<ul></ul><p><a class="user active user-mention" href="https://redmine.ruby-lang.org/users/710">@zverok (Victor Shepelev)</a> are you making available the code in <a href="https://github.com/zverok/enumerator_generate" class="external">https://github.com/zverok/enumerator_generate</a> available under the same licence as Ruby? We'd like to just run that proof-of-concept code as-is in TruffleRuby (it still passes all the specs.)</p> Ruby master - Feature #14781: Enumerator.generatehttps://redmine.ruby-lang.org/issues/14781?journal_id=884902020-11-14T21:34:47Zzverok (Victor Shepelev)zverok.offline@gmail.com
<ul></ul><p><a class="user active user-mention" href="https://redmine.ruby-lang.org/users/8671">@chrisseaton (Chris Seaton)</a> I do!<br>
Everything that is good for the community is good by me.</p>