inopinatus
4 months ago
For this audience it may be worth noting that Ruby’s blocks are closures and are passed to methods either anonymously/implicitly or as a named parameter, may be subsequently passed around to any collaborator object, or otherwise deferred/ignored, have the same range of argument arity as methods and lambdas, can even be formed from (and treated similarly to) lambdas, and are thereby fundamental to Ruby’s claim to being a multiparadigm language even as they also betray the Smalltalk roots.
In addition they have nonlocal return semantics, somewhat like a simple continuation, making them ideal for inline iteration and folding, which is how most new Rubyists first encounter them, but also occasionally a source of surprise and confusion, most notably if one mistakenly conflates return with result. Ruby does separately have callcc for more precise control over stack unwinding, although it’s a little known feature.
rubyfan
4 months ago
These were some of the best long running sentences I’ve read in a while. A true rubyist!
rubybaby99
4 months ago
[flagged]
inopinatus
4 months ago
The hard part is doing without semicolons.
dragonwriter
4 months ago
> can even be formed from (and treated similarly to) lambdas
They are also used to create lambdas (even the shorthand stabby-lambda syntax desugars to a call to Kernel#lambda with a block.)
> Ruby does separately have callcc for more precise control over stack unwinding, although it’s a little known feature.
callcc is included in CRuby but has been sidelined from Ruby as a language separate from CRuby as an implementation for a while, with Fibers understood to cover the most important use cases for callcc.
OptionOfT
4 months ago
As someone who comes from strict languages (the more strict, the better) Ruby blocks are... not fun.
I've seen them used in situations where they are used like a callback, but due to the nature of how you write them, you have no clue whether the variable you're referring to is a local or a global one.
This makes debugging incredibly hard.
inopinatus
4 months ago
Ruby isn’t strict, no, but that’s by the by, because this doesn’t sound like a problem with blocks or how you write them. It sounds more like a problem with evals i.e. some library or framework misusing them. Blocks are closures and they straightforwardly bind variables and resolve constants/instance variables from the context of their instantiation, and resolve methods similarly because self within the block is from instantiation, when we call them normally with yield or Proc#call. Same goes for implicit contexts used for definition, if your block does a bit of metaprogramming.
If someone plays silly buggers and invokes them under instance_eval or class_exec etc that fiddle with self or definition contexts then some of this goes out the window, but those are special-purpose methods that come papered with red flags. This is typically seen in poorly designed DSLs that are trying too hard to pretend they’re not actually Ruby. If memory serves, the Chef DSL was a prime example in this regard. If the language was stricter, then sure, this wouldn’t be possible. But debugging these cases isn’t super hard either once you know the limited range of culprits, and the fix is always the same: place values in local stack variables to rely on them in a closure.
Using blocks for callbacks is fine. Don’t make assumptions about the semantics of flow control statements that other languages may have imposed on you, i.e. use next and break for explicit block results and local exit instead of return, and don’t eval them.
user
4 months ago