Search code examples
lazy-evaluationcrystal-langenumerator

Implementing Lazy Enumerator in Crystal


In Ruby to build a custom lazy enumerator, one can utilize Enumerator like this:

enum = Enumerator.new do |e|
  e << value = ".a"
  loop { e << value = value.next }
end

enum.next # => ".a"
enum.next # => ".b"
enum.next # => ".c"
enum.rewind
enum.next # => ".a"

What's the Crystal's idiomatic way to imitate such a thing?


Solution

  • A bit wordier... Look at Iterator<T>

    class DotChar
      include Iterator(String)
    
      @current : String = ""
    
      def initialize
        @start = ".a"
        rewind
      end
    
      def next
        @current.tap { @current = @current.succ }
      end
    
      def rewind
        @current = @start
      end
    end
    
    e = DotChar.new
    p e.next # => ".a"
    p e.next # => ".b"
    p e.next # => ".c"
    e.rewind
    p e.next # => ".a"
    

    (Can't use enum as the identifier, as that's a keyword in Crystal.)

    You can do it much simpler if you sacrifice rewind:

    s = ".a"
    e = Iterator.of { s.tap { s = s.succ } }
    

    There may be in the future a way to do exactly as in Ruby, but it is a work in progress (that I hope wasn't abandoned yet, it seems to have stalled half a year ago). See this issue and this pull request for more info.