やわらかテック

興味のあること。業務を通して得られた発見。個人的に試してみたことをアウトプットしています🍵

Ruby3.4で追加されるブロックパラメーター"it"について

先週、届いたRuby Weeklyに面白そうなトピックが紹介されていました。

Ruby 3.4 Will Have 'it'
— There’s never been any doubt: Ruby has it. But now as well as the ‘it’ factor,
it really will have it. Confused? it is a nicer looking shortcut to the first parameter in a block (a la _1) – e.g. arr.each { puts it }.
Ruby 3.3 will warn on it with 3.4 (due in a year’s time) getting the real deal.
TAKASHI KOKUBUN

Re-reconsider numbered parameters: it as a default block parameter

ふむふむ...。
要するにRuby3.4系では、ブロック構文のデフォルトパラメーターにitが設定されてブロック引数をしてしなくても要素の参照が可能になるということです。

# 3.4系だとOK
[1,2,3].each do 
  puts it
end

なぜ追加されるのか

正直な所、ブロック引数のデフォルトパラメーターが設定されることの何が嬉しいのか個人的にはよく分かりませんでした。ブロック引数を指定すれば良いだけの話です。 Ruby Weeklyを見た時点では新機能についてのトピックなのかと思いましたが、Ruby Masterを見た所、既存機能の改善に関する提案でした。
実はすでにブロック構文でブロック引数を省略しても、_1, _2...(Numbered parameters)のように値を参照することができるようです。自分はこの機能について全く知りませんでした。

[1,2,3].each do
  puts _1 # 1 2 3
end

ただNumbered parametersを積極的に使いたいのかというと微妙ですね。
Ruby Masterでも議論されていましたが、_が変数名に付与されていると未使用変数のように感じられますし、ナンバリングされていることによってブロック引数の順序を理解している必要があります。その時点で自分ならブロック引数を明示的に宣言したいです。
ブロック構文が多くの場合、1つのブロック引数を取るためデフォルトパラメーター(_1)が便利なのは分かりますが、見た目が良くないのでitがあったほうが良いという意見は確かに...と感じました。
個人的には_1よりはitの方が視認性が良いと感じました。

バージョンによる動作の違い

Ruby Masterには3.3系ではitをデフォルトパラメーターとして使用すると警告が表示されて、3.4系ではブロック内部でデフォルトパラメーターitの参照が可能になると記載がありました。まだ3.4系は公開されていないので動作を確認することはできませんが、3.3系は試すことができました。

Ruby: 3.3.0-dev

Ruby Masterの記載通り、警告が表示されました。
ただしitが参照できないためにエラーになりました。

[1,2,3].each do
  puts it
end

(none):6: warning: `it` calls without arguments will refer to the first block param in Ruby 3.4; use it() or self.it
it-verification.rb:6:in `block in <main>': undefined local variable or method `it' for main (NameError)

  puts it
       ^^
        from it-verification.rb:5:in `each'
        from it-verification.rb:5:in `<main>'

ブロック引数にitを指定した場合にも警告が出力されるのかと思いましたが、思い違いでした。
Rubyはレキシカルスコープなので、ブロック引数にitが指定されていればデフォルトパラメーターのitを上書きしているのでしょうか。それならば警告が出力されないのも納得です。

[1,2,3].each do |it|
  puts it
end

# 1, 2, 3

Ruby: 3.2.2

警告が表示されず、単にエラーになりました。

it-verification.rb:6:in `block in <main>': undefined local variable or method `it' for main:Object (NameError)

  puts it
       ^^
        from it-verification.rb:5:in `each'
        from it-verification.rb:5:in `<main>'

もちろん、ブロック引数をきちんと宣言すればエラーになりませんでした。

[1,2,3].each do |it|
  puts it # 1 2 3
end

似たような機能を持つ言語はあるのか

itのようにデフォルトパラメーター(暗黙的な引数)を受け取る言語がいくつかあるようです。
自分が調べた限りだと、Ruby Masterでも紹介されていたKotlin。他にはGroovyとSwiftに同じような機能が実装されているようでした。

fun main() {
    val numbers = listOf(1, 2, 3, 4, 5)
    numbers.forEach { println(it) }
}
def numbers = [1, 2, 3, 4, 5]
numbers.each { println it }
let numbers = [1, 2, 3, 4, 5]
numbers.forEach { print($0) }

JVM上で動作するKotlinとGroovyはit。Swiftは$0が参照可能でした。
Swiftは現在のRubyのNumbered parametersと非常によく似たデフォルトパラメーターを提供していました。
やはりitの方が視認性が良いですね。引数を2つ以上取る場合であれば、明示的に引数を宣言すれば良いですし、そういったケースでitのようなデフォルトパラメーターを使うという方針が個人的には違和感があります。

Ruby3.4でitが使えるのが楽しみです。
少しでも「ええな〜」と思ったらはてなスター・はてなブックマーク・シェアを頂けると励みになります。