Python is a lovely language, but context managers often feel like a shabby substitute for what I can do with `yield` in Ruby :-)
Python is a lovely language, but context managers often feel like a shabby substitute for what I can do with `yield` in Ruby :-)
As for functions, Ruby doesn’t really have them—or, rather, blocks and Procs really are the closest you get to a function. Instead, since everything is an object, Ruby has methods instead of functions. Critically, because methods are defined on objects they have inherent object context, which is why you can’t treat a defined method as an anonymous function. Of course, there are ways to extract the method as a Proc and pass it to another object (with or without its object context). But having methods be objects the way they are in, for example, JavaScript would require extra magic under the hood, and lead to all sorts of context gotchas, which is probably why it doesn’t work that way.
If you’re prototyping a new interpreted language and don’t want to have to build a JIT that can optimize code, but still want some semblance of good performance, the block approach Ruby picks can be pretty useful.
Ruby doesn’t haven to allocate a full-on, garbage-collected, closure object every time a function accepts a block: it only has to do this if the block gets stored to a variable. If the block is only ever yield’d to, the allocation can be skipped.
And when your language’s primary looping mechanism is done with blocks, the difference adds up:
xs.each do |ys|
# with normal closures and no fancy JIT,
# the VM has to allocate a closure once per loop:
ys.each do |y|
end
end
Ruby was able to get away with its closure-heavy standard library APIs without a JIT for almost 3 decades because of the affordances that blocks provide over procs/lambdas.Ruby doesn't have functions at all - it has objects with methods, and you can treat a block as an object whenever you want.
IMO the ruby one is more powerful because it effectively allows you to customize language syntax by supplying a block. And it fits nicely with the stdlib and libraries (ex map yields to a block to do the mapping). Ruby also has an enumerator that does some similar things to pythons yield: https://docs.ruby-lang.org/en/master/Enumerator.html
Pythons yield is more commonly used for iteration and stuff although it does allow some neat tricks for scoping things.