Project

General

Profile

Actions

Feature #20664

open

Add `before` and `until` options to Enumerator.produce

Added by knu (Akinori MUSHA) 10 days ago. Updated 10 days ago.

Status:
Open
Assignee:
-
Target version:
-
[ruby-core:118784]

Description

Enumerator.produce provides a nice way to generate an infinite sequence but is a bit awkward to define how to end a sequence. It lacks a simple and easy way to produce typical finite sequences in an intuitive syntax.

This proposal attempts to solve the problem by adding these two options to the method:

  • before: when provided, it is used as a predicate to determine if an iteration should end before a generated value gets yielded.
  • until: when provided, it is used as a predicate to determine if an iteration should end until after a generated value gets yielded.

Any value that responds to to_proc and returns a Proc object is accepted in these options.

A typical use case for the before option is traversing a tree structure to iterate over the ancestors or following/preceding siblings of a node.

The until option can be used when there is a clear definition of the "last" value to yield.

enum = Enumerator.produce(File, before: :nil?, &:superclass)
enum.to_a #=> [File, IO, Object, BasicObject]

enum = Enumerator.produce(3, until: :zero?, &:pred)
enum_to_a #=> [3, 2, 1, 0]

Files


Related issues 2 (1 open1 closed)

Related to Ruby master - Feature #14781: Enumerator.generateClosedActions
Related to Ruby master - Feature #20625: Object#chain_ofOpenActions
Actions #1

Updated by knu (Akinori MUSHA) 10 days ago

Actions #2

Updated by knu (Akinori MUSHA) 10 days ago

Updated by zverok (Victor Shepelev) 10 days ago

I am not sure about this API.

I think in language core there aren’t many APIs that accept just a symbol of a necessary method (only reduce(:+) comes to mind, and I am still not sure why this form exists, because it seems to have been introduced at the same time when Symbol#to_proc was, so reduce(:+) and reduce(&:+) were always co-existing).

Mostly callables are passed as a block (and therefore there can be only one); but some APIs accept another callable (any object with #call method, like Enumerator.new).

So, what if condition is not an method of the sequence?.. Should we accept callables, too? Or, what if the method’s user expects it to be a particular value (like until: 0), or a pattern (like before: 0..1).

The alternative is

Enumerator.produce(File, &:superclass).take_until(&:nil?)

...which is more or less the same character-count-wise, more powerful (any block can be used), and more atomic.

The one problem we don’t currently have neither Enumerable#take_until, nor Object#not_nil?, to write something like

# this wouldn’t work
Enumerator.produce(File, &:superclass).take_while(&:not_nil?)
# though one can use
Enumerator.produce(File, &:superclass).take_while(&:itself)
#=> [File, IO, Object, BasicObject]

...but in general, I suspect adding Enumerable#take_until to handle such cases (and #take_while_after while we are on it :)) might be more powerful addition to the language, useful in many situations.

Updated by knu (Akinori MUSHA) 10 days ago

This proposal is based on the potential use cases I have experienced over the years. I've rarely seen a need for infinite sequences that can be defined with produce, and that is why I want to give produce() a feature-complete constructor.

Almost all sequences have had clear and simple end conditions. Traversing a tree structure for ancestor or sibling nodes would be the most typical use case, and the predicates like nil? and root? are mostly enough. Type-based conditions and inclusion conditions are not much seen probably because sequences are likely to be homogeneous and there is rarely more than one or a range of terminal values.

Updated by knu (Akinori MUSHA) 10 days ago

These options should take callables in this proposal. Procs and Methods certainly meet the condition: "Any value that responds to to_proc and returns a Proc object is accepted in these options".
The implementation does not bother to call to_proc on Procs, though.

Actions

Also available in: Atom PDF

Like1
Like0Like0Like0Like0Like0