Ruby 2.7 — Numbered Parameters

Brandon Weaver
Mar 18, 2019 · 3 min read

Ruby 2.7 is coming out this December, as with all modern releases, but that doesn’t stop us from looking for and writing about all the fun things we find in the mean time! No no no.

Image for post

For this article, we have something that’s very reminiscent of Bash, Perl, and Scala: Numbered parameters.

The Short Version

If you have a simple block with positional arguments, especially single positional, you can do the following:

[1, 2, 3].map { @1 + 3 }
=> [4, 5, 6]

…where @1 is the first parameter to the block function.

The Discussion

If you’d like to see the discussion behind this feature, it can be found here:


As this exposes the object directly, a majority of the examples are just slight adjustments to what you might be familiar to already. Let’s take a look at some of the examples in the test code:

Numbered parameters are only valid when referenced inside of a block:

assert_syntax_error('@1', /outside block/)
assert_valid_syntax('proc {@1}')

That means this is valid:

proc { @1 }
=> #<Proc:0x0000000111d42990@(pry):6>

…and this will cause a syntax error:

SyntaxError: (eval):2: numbered parameter outside block

Now the error is slightly modified from older Ruby versions in that it recognizes the second-use of the instance variable-like syntax and lets us know we used it outside of a block.

A numbered param has to follow a few rules, namely there are only numbers in it and 0 along with leading 0s are errors:

assert_syntax_error('proc {@01}', /leading zero/)    assert_syntax_error('proc {@1_}', /unexpected/)

That also means it’s going to do bad things if you try and use underscores for longer numbers:

(1..1_000_000).each_slice(1_000).map { @1 + @1_000 + @2 + @3 }
SyntaxError: unexpected local variable or method, expecting '}'
..._slice(1_000).map { @1 + @1_000 + @2 + @3 }
... ^~~~
(eval):2: numbered parameter outside block
...e(1_000).map { @1 + @1_000 + @2 + @3 }

Say we have collections or even hashes, we can use @2 and further if we need them to get at the specific values:

assert_equal(3, eval('[1,2].then {@1+@2}'))
assert_equal("12", eval('[1,2].then {"#@1#@2"}'))

For hashes this means you can access the key and the value:

{name: 'foo', age: 42}.map { [@1, @2] }
=> [[:name, "foo"], [:age, 42]]

If you had groups of three you could even start using more:

(1..9).each_slice(3).map { @1 + @2 + @3 }
=> [6, 15, 24]

…though it may be ill-advised to start getting into too many of these numbered params, as eventually you run out.

There is a limit, but it’s rather high:

assert_syntax_error('proc {@9999999999999999}', /too large/)

Defined here in this constant:

#define NUMPARAM_MAX 100 /* INT_MAX */

Though there should be warning signs that you’re doing something odd before you get anywhere close to this number.

Currently Pry will just give up if you try, and expect more input:

[13] pry(main)> (1..1_000_000).each_slice(1_000).map { @101 }
[13] pry(main)*

Ruby doesn’t like mix-and-match with our current way of doing block parameters:

assert_syntax_error('proc {|| @1}', /ordinary parameter is defined/)    assert_syntax_error('proc {|x| @1}', /ordinary parameter is defined/)

If you decide to use this, know that it’s one or the other, not both.

It would be good to remember that @1 and friends are just Ruby objects, meaning we can call anything on them that we would a parameter:

[{name: 'foo'}, {name: 'bar'}].map { @1[:name] }
=> ["foo", "bar"]
[{name: 'foo'}, {name: 'bar'}]
.map { }
.map { }
=> ["foo", "bar"]

Though in the last example, it would be good to remember the current shorthand syntax of map(&:name) instead.

Wrapping Up

This is definitely a very interesting feature, though I certainly feel Matz when he says the following:

I still feel weird when I see @ and @1 etc. Maybe I will get used to it after a while.
I need time.

- Matz.

I wonder what new things it will lead to, but I’m excited nonetheless to see what else people can use it for.

2.7 is already off to an interesting start, let’s see where it goes from here.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store