It's just a simple question, how is y.<< method is able to halt the code-block mid execution ??
I have expected the code block to run only once and never halt in the middle :/
e = Enumerator.new do |y|
puts "Ruby"
y << 1
y << 2
puts "Ruby"
y << 3
end
puts e.each.next
puts e.each.next
puts e.each.next
e.rewind
puts e.each.next
puts e.each.next
puts e.each.next
Almost all Ruby implementations are Free Software and Open Source, so you can just look at the source code to see how it is implemented.
In Rubinius, the most interesting part is
Enumerator::Iterator#reset, implemented incore/enumerator.rb:and
Enumerator::Iterator#next:TruffleRuby's implementation is very similar, as you can see in
src/main/ruby/truffleruby/core/enumerator.rb:JRuby is also very similar, as you can see in
core/src/main/ruby/jruby/kernel/enumerator.rb:MRuby's implementation is very similar, as you can see in
mrbgems/mruby-enumerator/mrblib/enumerator.rb.YARV also uses Fibers, as can be seen in
enumerator.c, for example here:So, not surprisingly,
Enumeratoris implemented usingFibers in many Ruby implementations.Fiberis essentially just Ruby's name for semi-coroutines, and of course, coroutines are a popular way of implementing generators and iterators. E.g. CPython and CoreCLR also implement generators using coroutines.One exception to this seems to be Opal. My assumption was that Opal would use ECMAScript Generators to implement Ruby
Enumerators, but it does not look like that is the case. The implementation of RubyEnumerators in Opal is found inopal/corelib/enumerator.rb,opal/corelib/enumerator/generator.rb, andopal/corelib/enumerator/yielder.rbwith some help fromopal/corelib/runtime.js, but unfortunately, I don't fully understand it. It does not appear to use either RubyFibers or ECMAScript Generators, though.By the way, your usage of
Enumerators is somewhat strange: you callEnumerator#eachsix times without a block, but callingEnumerator#eachwithout a block just returns theEnumeratoritself:So, in other words, all those calls to
Enumerator#eachare just no-ops. It would make much more sense to just callEnumerator#nextdirectly: