skip to content

In Ruby, which of proc, Proc.new, lambda and the -> literal create lambdas, and how do you check one?

level: juniorimportance: should knowfreq 50%

answer

  1. two constructors each
  2. one class for all four
  3. Proc#lambda? is the check
  4. lambda needs a literal block
  5. no block: ArgumentError

basics

~10 s

lambda { } and ->(x) { } create lambdas; proc { } and Proc.new { } create regular procs. All four return a Proc, so check with Proc#lambda?, which is true only for lambdas.

solid answer

~30 s

`lambda { }` and the stabby literal `->(x) { }` create lambdas; `Kernel#proc` and `Proc.new` create regular procs, and so does a captured block. All of them are instances of `Proc`, so `is_a?(Proc)` or `class` cannot tell them apart, but `Proc#lambda?` can: `->(x) { x }.lambda?` is `true`, `proc { |x| x }.lambda?` is `false`. Lambda-ness is fixed at creation and cannot be converted: since Ruby 3.3, `lambda(&some_proc)` with a regular proc raises `ArgumentError` (`the lambda method requires a literal block`), and `proc(&some_lambda)` returns the same lambda unchanged. `proc` and `Proc.new` without a block raise `ArgumentError` too.

code

ruby · 10 lines
ruby
presence = ->(value) { !value.nil? }
legacy   = proc { |value| !value.nil? }

presence.lambda?          # => true
legacy.lambda?            # => false
presence.class            # => Proc

proc(&presence).lambda?   # => true, same object returned
lambda(&legacy)           # ArgumentError: the lambda method requires a literal block
Proc.new                  # ArgumentError: tried to create Proc object without a block

go deeper

for a junior

Recall the pairs: lambda and -> make lambdas, proc and Proc.new make regular procs, and lambda? tells them apart.

for a middle

Explain that the kind is a flag on one class, fixed at creation, and what it changes: argument checking and where return goes.

for a senior

Catch upgrade breakage: lambda(&proc) raising since Ruby 3.3, and blockless Proc.new relying on long-deprecated block capture.

for a principal

Decide how a library accepts callables: duck-type on call, build its own as lambdas, and check lambda? only where semantics truly matter.

## Four constructors, one class Ruby has one class for packaged blocks of code, `Proc`, and four everyday ways to make one: | Expression | Kind | `lambda?` | |---|---|---| | `proc { \|x\| x }` | regular proc | `false` | | `Proc.new { \|x\| x }` | regular proc | `false` | | `lambda { \|x\| x }` | lambda | `true` | | `->(x) { x }` | lambda | `true` | - **`Kernel#proc`** is documented as equivalent to `Proc.new`. - **`Kernel#lambda`** is equivalent to `Proc.new` except that the result checks the number of arguments it receives. - **`->`** is the lambda literal. Parameters usually go in parentheses before the body: `->(value, field) { ... }`, or `-> { ... }` with none. - A **block captured** with an `&block` parameter is a regular proc as well. The kind decides two behaviours: whether arguments are checked strictly, and whether `return` leaves the callable or its enclosing method. ## Checking the kind with Proc#lambda? Because every one of these objects is a `Proc`, class checks cannot tell them apart: - `->(x) { x }.class` and `proc { |x| x }.class` both return `Proc`. - `is_a?(Proc)` is `true` for both. - `Proc#lambda?` returns `true` for a lambda and `false` for a regular proc. It is the only direct check. When `inspect` prints a lambda, Ruby adds `(lambda)` to the description, which helps when debugging in `irb`. ## Lambda-ness cannot be converted The flag is set when the object is created and stays with it: 1. **`proc(&a_lambda)`** and **`Proc.new(&a_lambda)`** return the lambda itself, and `lambda?` is still `true`. 2. **`lambda(&a_regular_proc)`** raises `ArgumentError` with the message `the lambda method requires a literal block` on Ruby 3.3 and later. Ruby 3.0 to 3.2 returned the regular proc unchanged, warning only under the deprecated warning category, so the call looked like a conversion but did nothing. 3. **`lambda(&:upcase)`** is allowed, because a symbol block is accepted. 4. **Passing a lambda with `&`** to a method does not make it lenient; the method yields into lambda semantics. If you need a lambda, write the lambda where the code is: `->(v) { ... }` or `lambda { |v| ... }` with a literal block. ## Blockless calls On Ruby 4.0, `proc`, `Proc.new` and `lambda` all need a block. `Proc.new` and `proc` without one raise `ArgumentError` with `tried to create Proc object without a block`. Very old code relied on a blockless `proc` inside a method capturing that method's block; Ruby 2.7 deprecated that, and the replacement is an explicit `&block` parameter. ## Picking a constructor In a form library that stores validation rules as callables: - Write rules as **`->(value) { ... }`**. The literal is short and the resulting lambda checks its arguments. - Use **`lambda { |value| ... }`** when a multi-line `do ... end` body reads better; it builds the same kind of object. - Reserve **`proc`** for the rare case where you want block leniency in a stored object. - When accepting callables from users of the library, check `respond_to?(:call)` rather than the class, and consult `lambda?` only if your code truly depends on strict arity.

  • What happened to lambda(&some_proc) before Ruby 3.3?
    From Ruby 3.0 it warned under the deprecated warning category and returned the regular proc unchanged, so the result still had lenient arguments and a method-leaving `return`. Ruby 3.3 made it raise `ArgumentError`, turning the silent non-conversion into an error.
  • Does proc(&a_lambda) produce a regular proc?
    No. When `proc` or `Proc.new` receives an existing `Proc` object of class `Proc` as its block, it returns that object, so the lambda comes back unchanged and `lambda?` stays `true`.

saying these in an interview costs you the question

  • Proc.new with a block returns a lambda
  • Lambdas are instances of a separate class
  • lambda(&some_proc) converts a regular proc into a lambda
  • is_a?(Proc) returns false for a lambda
  • Proc.new without a block captures the method's block