[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)