[HN Gopher] What Is the Difference Between a Block, a Proc, and ...
___________________________________________________________________
What Is the Difference Between a Block, a Proc, and a Lambda in
Ruby? (2013)
Author : Tomte
Score : 62 points
Date : 2025-05-18 18:34 UTC (3 days ago)
(HTM) web link (blog.awaxman.com)
(TXT) w3m dump (blog.awaxman.com)
| vidarh wrote:
| Separating out blocks this way is pretty meaningless. Blocks are
| a syntactical construct that can be optimized a certain way, and
| is _in MRI_.
|
| You can obtain a value of a block by naming it, and when you do,
| what you obtain is a proc.
|
| A Ruby implementation could if it chooses make any block a proc,
| because you shouldn't be able to tell the difference without
| extensive contortions (e.g. you could iterate over ObjectSpace
| and show that a block causes the creation - or not - of an object
| of the Proc class). And in-fact my (woefully buggy and
| incomplete) Ruby compiler prototype does just that.
| HellzStormer wrote:
| For those unaware, Ruby blocks (and procs) are more flexible than
| anonymous functions as most language implement them. The article
| briefly goes over that, mentioning that lambda (regular anonymous
| functions) and procs (blocks) don't treat execute `return` the
| same way. There are also particularities with regard to next
| (continue) and break.
|
| I made a post about the niceties of blocks:
| https://maxlap.dev/blog/2022/02/10/what-makes-ruby-blocks-gr...
| jbverschoor wrote:
| Which is exactly what I don't like about them. They should've
| used a different keyword or something.
|
| This runs you into problems similar to missing a break in such
| statements with many language
| chowells wrote:
| Flexible in a way, sure. But non-locality is generally a bad
| property, not good. Adding it is a workaround for all the
| enumeration methods using blocks in a way that makes people
| think they're weird looping syntax instead of a fundamentally
| different idea.
|
| People want to do early returns from looping over a collection,
| so take the easy solution of adding more messy language
| semantics instead of finding a semantically simple solution
| instead. (For that matter, have fun working out the semantics
| of break and next when used in a block that isn't an argument
| to an enumeration method. How do you as a method author opt in
| to distinguishing between the two after yielding to a block?)
|
| This is generally the case with Ruby everywhere. Why does that
| thing have an edge case with weird semantics? To work around
| the edge case with weird semantics somewhere else. It's all
| fine if you want to just try things until something works. But
| if you want to really understand what's going on and write code
| that's a first-class participant in the language features, it
| gets really frustrating to try to deal with it all.
| vidarh wrote:
| It's really not that complex.
|
| For my (buggy, unfinished, languishing without updates)
| prototype Ruby compiler, lambda and proc (and blocks) are all
| implemented nearly the same way, with the exception that for
| proc/blocks, return/break/next will act as if having unwound
| the stack to the scope where the proc was defined first (or
| throw an error if escaped from there).
|
| The distinction is very obvious when you think of it in that
| way - a proc acts as if called in the context/scope it was
| defined, while a lambda acts as if called in the scope it is
| called in.
|
| > How do you as a method author opt in to distinguishing
| between the two after yielding to a block?
|
| You don't. A block acts as a proc, not a lambda. If you want
| lambda semantics, then take a lambda as an argument - don't
| take a block/proc and be surprised it doesn't act like
| something it isn't.
| chowells wrote:
| Given that you completely ignored what I said I wanted to
| do and gave an answer for some other question, I'm pretty
| sure it's more complicated than you think.
|
| I want to write a method that takes a _block_ and
| distinguishes between next and break, exactly like the
| methods of enumeration do. It 's obviously possible because
| a super common interface does it.
|
| Last time I looked, that interface does it by being written
| in native code that interfaces with the interpreter. That
| is, it's not part of the language semantics. It's a special
| case with weird rules unlike what anything else gets to do.
|
| Or at least it was. Maybe the language has actually made it
| accessible since then, but I'm not optimistic. That's not
| the ruby way.
| drnewman wrote:
| It seems you're pretty upset about your experience with Ruby.
| I'm sorry that's been the case for you.
|
| However, in Ruby blocks aren't just about flexibility, more
| importantly they're about generality. They're not there to
| resolve an edge case at all (Ruby also has keywords for
| loops). They're a generalization of control flow that is just
| slightly less general than continuations. In practical use
| they provide an alterative to Lisp macros for many use cases.
|
| These were some of the problems Matz was trying to sort out
| with his design of Ruby--creating a language that was fun and
| easy to use for day-to-day programming, with much of the
| meta-programming power of Lisp and Smalltalk. Blocks are one
| of his true innovatations that came from trying to balance
| that tension.
| paulddraper wrote:
| tl;dr
|
| A block is a part of the AST. Like a pair of braces.
|
| A proc is a function.
|
| A lambda is a proc that treats args and returns differently.
| endorphine wrote:
| > Lamdas check the number of arguments, while procs do not
|
| FWIW, Matz himself called this difference "a design mistake".
| weaksauce wrote:
| which way would he have liked to be the "correct way"
| kubb wrote:
| What happens when you return from a proc when the enclosing
| function has already returned?
| lcnPylGDnU4H9OF wrote:
| You can't call return from inside a Proc inside a method; you
| will encounter a LocalJumpError.
| vidarh wrote:
| You can call return from inside a proc all you want. You
| can't do so if you _return the proc_ to an outside scope
| without getting LocalJumpError.
|
| E.g, given: def foo = proc do 42 end
| def bar = proc do return 42 end
|
| Then `foo.call` is fine, but `bar.call` will indeed give a
| LocalJumpError as you say.
|
| But a return in a proc that hasn't escaped its defining scope
| is fine: def baz = proc do return 42
| end.call
|
| Calling `baz` here will just return 42 to the surrounding
| scope.
| vidarh wrote:
| This example: def foo proc do
| return 42 end.call end def bar
| proc do return 42 end end
| p foo p bar.call
|
| Will produce: 42 test.rb:11:in `block
| in bar': unexpected return (LocalJumpError)
|
| from test.rb:16:in `<main>'
|
| So in other words, it's handled.
| whalesalad wrote:
| This is the biggest wart on the Ruby language by far.
___________________________________________________________________
(page generated 2025-05-21 23:01 UTC)