Numbered Block Parameters
Fast recall
Quick revision
Core answer
_1 means its first argument, _2 the second, through _9. For example, prices.map { _1 * 1.18 } is equivalent to prices.map { |price| price * 1.18 }. Ruby does not allow numbered parameters and an explicit |...| list in the same block. They are clearest in short, obvious expressions; named parameters are better when the block is longer or the roles of multiple values need explanation.Whether you know numbered parameters (Ruby 2.7+) — _1, _2, ... in blocks without declaring params.
Mixing _1 and named params (error), or overusing them for complex blocks (hurts readability).
Numbered block params (Ruby 2.7+): _1, _2 in blocks without |x|. Shorthand for tiny blocks. reduce: _1 + _2. Don't mix with named params.
Interview-ready guide
Interview answer
Numbered block parameters let a Ruby block refer to its yielded arguments as _1, _2, and so on without writing a |parameter_list|. They were added in Ruby 2.7 as concise syntax; they do not change how the block receives values or closes over its surrounding scope.
For example, users.map { _1.email } is the short form of users.map { |user| user.email }. With each_with_index, _1 is the element and _2 is its index because that is the order the method yields them. The method's yield contract still decides what every position means.
Ruby rejects a block that mixes numbered parameters with explicit block parameters. Assignment to _1 is also invalid. Nested blocks are especially easy to misread because each block has its own argument context and an outer numbered parameter cannot simply be reused inside a nested numbered block.
This shorthand works well for a one-step transformation or predicate where the role is obvious. In a multi-line calculation, a domain name such as invoice or running_total carries useful meaning that _1 and _2 cannot provide.
My rule is based on clarity rather than line count alone: use a numbered parameter when the reader can identify the input at a glance, and switch to explicit names as soon as positions require explanation.
Numbered Block Parameters
Direct Answer
Numbered block parameters are Ruby's shorthand for block arguments. When a block has no explicit parameter list, _1 means its first argument, _2 the second, through _9. For example, prices.map { _1 * 1.18 } is equivalent to prices.map { |price| price * 1.18 }. Ruby does not allow numbered parameters and an explicit |...| list in the same block. They are clearest in short, obvious expressions; named parameters are better when the block is longer or the roles of multiple values need explanation.
Deep Dive & Architecture
A block receives values from the method that yields to it. Numbered parameters remove the declaration between vertical bars, but they do not invent or reorder those values. In map, _1 is normally the current element. In each_with_index, the collection element is first and the numeric index is second. In a two-value iterator over a hash, the exact argument behaviour should be read from that method's contract rather than guessed from the variable names.
Ruby activates implicit numbered parameters only when the block has no explicit parameter declaration. The highest referenced position determines the apparent arity of the block. Writing { |value| _1 } tries to use two parameter styles in the same scope and is a syntax error. A numbered parameter is also reserved for input, so assigning to _1 is not permitted.
The concise form is helpful when the operation already names the role. temperatures.select { _1.negative? } reads naturally because temperatures and negative? provide the missing context. records.reduce({}) { _1.merge(_2.key => transform(_2)) } forces the reader to remember two roles while following other logic. Explicit |result, record| names make that version easier to verify and change.
Nesting introduces another boundary. Each block can establish its own numbered-parameter scope, and Ruby prevents ambiguous access where an outer block already relies on implicit parameters. Even when code can be rearranged to compile, _1 in two nearby scopes is visually fragile. Naming at least the outer value usually reveals the data flow and avoids accidental confusion during maintenance.
Numbered parameters remain ordinary block inputs with respect to closure behaviour. The block can still read local variables created outside it, and methods such as map, select, or reduce still determine the return value. Performance does not improve merely because the bars and name disappeared; this is a syntax and readability choice.
A useful review question is whether changing _1 to a sensible name adds information. If order tells the reader something that the collection and method do not, keep the name. If it only repeats an obvious one-line operation, the shorthand can remove noise. Consistency inside a codebase also matters more than demonstrating every available syntax feature.
Code Example
prices = [10, 25, 40]
with_tax = prices.map { _1 * 1.18 }
names = %w[Ada Linus]
names.each_with_index { puts "#{_2 + 1}. #{_1}" }
orders = [{ total: 20 }, { total: 35 }]
total = orders.reduce(0) do |running_total, order|
running_total + order[:total]
end
p with_tax
p total
Common Mistakes & Pitfalls
Mixing _1 and named params (error), or overusing them for complex blocks (hurts readability).
Check your understanding
Answer these without looking back at the article.
- Who decides whether `_2` is an index, value, or accumulator?
- Why are numbered parameters difficult to read in nested blocks?
- When does an explicit block parameter add useful domain meaning?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.