Hacker News new | past | comments | ask | show | jobs | submit login

Blocks and `yield` are a killer feature; I regularly pick Python over Ruby for similar reasons as you, but I consistently find myself missing those two (and all the things that get built out of them, like the `Iterator` APIs).

Python is a lovely language, but context managers often feel like a shabby substitute for what I can do with `yield` in Ruby :-)




This is basically how I feel about true first-class functions when I have to wrangle ruby and care about blocks, procs, and lambdas.


I love the syntax of Ruby blocks (IMO it's the best syntax for higher-order functions in any language) but I don't understand why they were implemented the way they are, where some things are blocks, some things are procs, and neither one is really a function. I would prefer if Ruby blocks were just functions.


Not saying it’s the optimal choice in the languages design spectrum, but it is a little defensible:

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.


You can convert between blocks and Procs very easily. Ruby has several methods to let you do that, both as syntax and as methods. They are really two sides of the same coin; a block can be thought of as a Proc that is intended for immediate usage (though this is convention, and of course nothing prevents you from converting a block into a Proc and stashing it away for later).

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.


In 15+ years of writing Ruby for production I can't think of a single time where the distinction between blocks and procs caused a problem. Blocks are a syntactic element, procs are a data type (that you can construct with a block). I honestly don't even know where the problem is supposed to come from.


It comes up when you get in “ok this block is great, but now I want two of them”. Only one can be a block, the other needs to be a Proc, and now there’s this odd syntactic distinction


Blocks are Proc objects whenever you take their value, and it's only an implementation dependent optimization that they are not that all the time in MRI. It's perfectly viable to implement a Ruby where blocks are always Proc's. You don't need to care about that distinction.

Ruby doesn't have functions at all - it has objects with methods, and you can treat a block as an object whenever you want.


Is Ruby's "yield" different than Python's "yield"? I use Python generators all the time, and I think they're the bee's knees.


It’s kinda different although you can do some similar things.

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.


Completely different. Ruby‘s yield calls a block, which is basically an anonymous method with some special syntax tacked on.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: