Automatic iterators!

…and there is a catch :wink:

The implementation is event-driven, in order to continue the enumerator loop as will, each time next is called.

class AutoIterator(T)
  include Iterator(T)

  class Stop < Exception

  @value_channel = Channel(T | Iterator::Stop).new
  @next_channel = Channel(Nil).new
  @proc : Proc(Proc(T, Nil), Nil)
  @first = true
  @end : Iterator::Stop | Nil = nil

  private def initialize(@proc)

  def : Proc(T, Nil) ->)
    new proc
  private def sender(value : T)
    @value_channel.send value
    raise if @end

  def next : T | Iterator::Stop
    if end_result = @end
      return end_result
    elsif @first
      spawn do ->sender(T)
        @value_channel.send(Iterator::Stop::INSTANCE) if !@end
      rescue ex : Stop
      @first = false
      @next_channel.send nil
    result = @value_channel.receive
    if result.is_a? Iterator::Stop
  def close
    @end = Iterator::Stop::INSTANCE

i = AutoIterator(Char).new do |sender|
  "ab".each_char do |char| char
puts "Starting"
puts #=> a

puts "Do some other stuff"
puts #=> b

puts "The end"
puts #=> Iterator::Stop

Obviously, this iterator is slower than the custom String::CharIterator returned by String#each_char (benchmarks shows 4x slower), but it is still useful in some cases.
When making a custom iterator is not worth the effort, especially when each iteration is expensive, AutoIterator may be a good option.

Beware it is more a POC than anything else, there are flaws. It’s there because it is possible :grin:

IIUC this would implement ?


What’s the use case, more background?

Ah, that brings back memories. I had something like this before we even had any Iterator :D

1 Like

Collection types currently have to provide two different implementations for iterating the items if you wont both a yielding method and an iterator: each(& : T ->) and each : Iterator(T) are completely separate. And the latter is usually more complex because it needs an extra iterator type to keep track of state.
If we had a reliable way to base an Iterator(T) on a method that yields T, it would be a huge improvement.
Currently, most standard collection types have both implementations. But many (especially outside of stdlib) don’t implement the iterator because it’s more work.

1 Like

It would be possible to autogenerate iterators from code that yield values. Of course, not any code with yield is convertible to an iterator, but with some restrictions it might happen someday. Maybe before Crystal 2.0, who knows :grin:

1 Like