3 ms·
> I Miss: Generators Excuse me, what?! Python has stackless symmetric coroutines; Ruby comes along and brings you stackful symmetric coroutines, what's there t
by DarkWiiPlayer 6y ago
> I Miss: Generators
Excuse me, what?! Python has stackless symmetric coroutines; Ruby comes along and brings you stackful symmetric coroutines, what's there to miss?
How is `return to_enum :each unless block` not infinitely nicer than pythons yield? (and waaay more powerful as a little cherry on top)
- orf 6y agoGenuinely no idea what your statement does, but “yield 123” or “yield from xyz” is infinitely more clear.
- regularfry 6y agoI think nestling somewhere in the core of the GP's comment is that Ruby also has `yield`, and using it (or related functionality) is pervasive.
- inopinatus 6y agoThat’s the idiom for an enumeration method called without a block. It’s why you can chain them, as in ary.map.with_index and so forth.
- vidarh 6y agoIt creates an Enumerator (for practical purposes consider it a continuation) over the method "each" unless you've passed in an argument named "block", which by convention would be a block argument (a closure passed after the argument list effectively) that will evaluate to Proc object. Consider this example: class Foo def each yield(42) yield(:hello) yield(:world) end end You can now do: Foo.new.to_enum(:each) (In this case the ":each" is redundant - to_enum defaults to :each, so you could do Foo.new.to_enum) And you will get back an Enumerator that supports the Enumerable interface, giving you methods like map, inject, to_a (to array) etc. as a facade to the original object so you can do (for example: Foo.new.to_enum.to_a => [42, :hello, :world] (you can achieve a similar thing by including Enumerable into the class in question, but to_enum lets you do this on arbitrary objects without messing around with the class) It allows any method to return something that behaves like a collection class without needing to pass more than a method that implements "each" by yielding each value of the collection. You can also do this with a bare lambda, btw., but the syntax is a bit awkward as "yield" is syntactic sugar over calling the "call" method over an implicit (unnamed) block argument that isn't quite as transparent as you might like: ob = ->(&block) { block.call(42); block.call(:hello); block.call(:world) }.to_enum(:call) The explicit "&block" argument is needed because "yield" has lexical scope where defined. "ob" is now an enumerator that supporters each, to_a, map etc.: ob.to_a => [42, :hello, :world]
- DarkWiiPlayer 6y ago> for practical purposes consider it a continuation But it's not though. While coroutines can be used to implement one-shot continuations and vice versa, they are very different features and behave completely differently.
- vidarh 6y agoWhile you're technically right, in terms of using it as a shortcut to give superficial understanding of how Enumerator functions the distinctions are largely irrelevant.
- DarkWiiPlayer 6y ago> “yield 123” or “yield from xyz” is infinitely more clear It's funny because the first is exactly how you write this in ruby.
- diarrhea 6y agoGenerators aren't only used in the context of coroutines.
- inopinatus 6y agoThe article misleadingly states “Ruby 3.0 has Fibers”. Not false, but Ruby 2.0 had fibers, they are used for building a variety of cooperative concurrency behaviours, and they go back years.
- DarkWiiPlayer 6y agoBut generators literally are coroutines though
- pmontra 6y agoNo idea what that statement does and I'm a rubyist. Anyway I think that Enumerator::Lazy is what one should use in most cases. Quite syntax heavy compared to Python though, but what is not in the very core of a language is always more verbose. https://ruby-doc.org/core-3.0.0/Enumerator/Lazy.html https://ruby-doc.org/core-3.0.0/Enumerator/Lazy.html
- inopinatus 6y agoHopefully you do actually recognise what that does: the standard library has many methods documented to return an enumerator if no block is given. This is how.