Remix.run Logo
judofyr 20 hours ago

Blocks are fundamentally different from functions due to the control flow: `return` inside a block will return the outer method, not the block. `break` stops the whole method that was invoked.

This adds some complexity in the language, but it means that it’s far more expressive. In Ruby you can with nothing but Array#each write idiomatic code which reads very similar to other traditional languages with loops and statements.

vidarh 14 hours ago | parent | next [-]

More specifically blocks (and "proc"'s) return from the defining scope. This is just a minor clarification, but it matters, because if you pass a block down from where it is defined, and the block calls "return" it will still not just exit from the method where it was called, but from the method where it was defined.

This can sometimes be useful: A calling method can pass down a block or proc to control if/when it wants an early return.

Basically Ruby has two types of closures:

* A return in a lambda returns to the calling scope. So basically, it returns to after where the "call" method is invoked.

* A return in a block or a proc returns from the scope in which it was defined (this is also why you get LocalJumpError if you try to return a block or a proc, but not a lambda to the method calling the one where the block or proc is defined).

When you name a block, you get a Proc object, same as you get when you take the value of a lambda or proc.

In practice, that blocks in MRI are not Proc objects already is just an implementation detail/optimisation. I have a long-standing hobby project to write a Ruby compiler, and there a "proc" and a bare block are implemented identically in the backend.

oezi 19 hours ago | parent | prev [-]

You are right on return (use next in a block), but break uses block scope.

judofyr 17 hours ago | parent [-]

Maybe I explained it a bit imprecise. I was trying to explain the following behavior:

    def foo
      p 1
      yield
      p 2
    end

    foo { break }
This only prints "1" because the break stops the execution of the invoked method (foo).
Mystery-Machine 14 hours ago | parent [-]

WAT? I'm a 12+ years Ruby developer and I didn't know this.