Project

General

Profile

Actions

Bug #17764

closed

ブロック付きメソッドにおけるproc(または Proc.new)の参照がArgumentErrorを吐く問題

Added by firelzrd (Masahito Suzuki) over 3 years ago. Updated 8 months ago.

Status:
Closed
Assignee:
-
Target version:
-
ruby -v:
ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-linux]
[ruby-dev:51033]

Description

ブロック付きメソッドにおけるproc(または Proc.new)の参照がArgumentErrorを吐く問題
Ruby 3.0.0 linux x86-64およびWindows上のmingw-x86-64バイナリにて発生し、2.7.2-1ではいずれも発生しないことを確認済み。
また、2021/3/31現在、TryRubyサイト( https://try.ruby-lang.org/ ) の上でも発生することを確認済み。

def foo
  puts (block_given? ? proc : proc{"ブロックなし"}).call
end

# @ruby 3.0.0
foo #ブロックなし => "ブロックなし"
foo{"ブロックあり"} #ブロックあり => ArgumentError: tried to create Proc object without a block

# @ruby 2.7.2
foo #ブロックなし => "ブロックなし"
foo{"ブロックあり"} #ブロックあり => "ブロックあり"

Files

argumenterror.png (125 KB) argumenterror.png TryRubyサイトでの実行結果のスクリーンショット firelzrd (Masahito Suzuki), 03/31/2021 12:40 AM

Updated by osyo (manga osyo) over 3 years ago

Ruby 3.0 からはブロック引数がない Proc.new / procArgumentError になるようになりました。

ブロックを指定しない lambda は Ruby 2.6 までは警告メッセージ「warning: tried to create Proc object without a block」が出力され、Ruby 2.7 では ArgumentError (tried to create Proc object without a block) が発生します。
ブロックを指定しない proc は、Ruby 2.7 では $VERBOSE = true のときには警告メッセージ「warning: Capturing the given block using Proc.new is deprecated; use &block instead」が出力され、Ruby 3.0 では ArgumentError (tried to create Proc object without a block) が発生します。
https://docs.ruby-lang.org/ja/2.7.0/method/Kernel/m/lambda.html

Updated by firelzrd (Masahito Suzuki) over 3 years ago

ご返答ありがとうございます。

本件は「ブロックを指定しない proc」についてではなく「ブロックを指定した proc」がどうもバグっているのではないかと思い報告させていただきました。
ブロック付きでメソッドをコールしたとき、メソッド内ではblock_given?の値がtrueとなり、proc (またはProc.new)で渡されたブロックを参照可能なはずですが、
サンプルプログラムの挙動でもお分かりいただけるように、block_given?==trueの時のprocの参照がArgumentErrorを吐いているというものです。

以上、お力添えをいただければ幸いです。

osyo (manga osyo) wrote in #note-1:

Ruby 3.0 からはブロック引数がない Proc.new / procArgumentError になるようになりました。

ブロックを指定しない lambda は Ruby 2.6 までは警告メッセージ「warning: tried to create Proc object without a block」が出力され、Ruby 2.7 では ArgumentError (tried to create Proc object without a block) が発生します。
ブロックを指定しない proc は、Ruby 2.7 では $VERBOSE = true のときには警告メッセージ「warning: Capturing the given block using Proc.new is deprecated; use &block instead」が出力され、Ruby 3.0 では ArgumentError (tried to create Proc object without a block) が発生します。
https://docs.ruby-lang.org/ja/2.7.0/method/Kernel/m/lambda.html

Updated by ko1 (Koichi Sasada) over 3 years ago

  • Status changed from Open to Closed

ブロック付きメソッドの中でブロックなし proc したとき、エラーになるようになった、というのが、3.0 での変更点です。

def foo
  proc
end
foo{} #=> `proc': tried to create Proc object without a block (ArgumentError)

Updated by firelzrd (Masahito Suzuki) over 3 years ago

ご返答ありがとうございます。
ご主旨、理解いたしました。ご教示ありがとうございます。お騒がせいたしました。

つきましては、差し支えなければ、
①ブロック引数として渡されたprocをメソッド内で変数として参照したい
②メソッド呼び出し時のブロック引数は省略可能にしておきたい
というニーズが両立する場合にどのようにすればよいか、ヒントをいただければ幸いです。

①を実現するだけならば&の付いたブロック引数を取ればよさそうですが…

あるメソッドにブロックが渡された場合にはprocをインスタンス変数に代入しておいて、後でcallbackする、という使い方を想定しています。
何卒ご教示を賜われれば幸いです。

以上よろしくお願いいたします。

ko1 (Koichi Sasada) wrote in #note-3:

ブロック付きメソッドの中でブロックなし proc したとき、エラーになるようになった、というのが、3.0 での変更点です。

def foo
  proc
end
foo{} #=> `proc': tried to create Proc object without a block (ArgumentError)

Updated by osyo (manga osyo) over 3 years ago

firelzrd (Masahito Suzuki) wrote in #note-4:

ご返答ありがとうございます。
ご主旨、理解いたしました。ご教示ありがとうございます。お騒がせいたしました。

つきましては、差し支えなければ、
①ブロック引数として渡されたprocをメソッド内で変数として参照したい
②メソッド呼び出し時のブロック引数は省略可能にしておきたい
というニーズが両立する場合にどのようにすればよいか、ヒントをいただければ幸いです。

①を実現するだけならば&の付いたブロック引数を取ればよさそうですが…

あるメソッドにブロックが渡された場合にはprocをインスタンス変数に代入しておいて、後でcallbackする、という使い方を想定しています。
何卒ご教示を賜われれば幸いです。

以上よろしくお願いいたします。

やりたいこととしてはこういうことでしょうか?

def foo(&block)
  # ブロック引数がない場合は block は nil になる
  puts (block ? block : proc{"ブロックなし"}).call
end

foo #ブロックなし => "ブロックなし"
foo{"ブロックあり"} # ブロックあり => "ブロックあり"

Updated by matz (Yukihiro Matsumoto) over 3 years ago

「メソッド呼び出し時のブロック引数は省略可能にしておきたい」という要求の正確な意味がわからなかったのですが、これが「引数リストに&blk変数を指定したくない」というものだとすれば、それは3.0からできなくなりました。これまではprocメソッドを引数なしで呼び出す方法でメソッドに渡されたブロックをProcとして取り出せていましたが、暗黙のコンテキスト参照が入りますし、読解の点からも望ましくないと考えての変更です。「&blk引数」を使ってください。それに、たぶんこっちのほうが高速です。

Matz.

Updated by firelzrd (Masahito Suzuki) over 3 years ago

ご教示大変ありがとうございます。
希望した動作ができました。

恥ずかしながら、ブロック引数の指定がない場合に&blkがnilとなる挙動を把握できていませんでした。
さらに一時遅いと言われたことのある&引数は従来のprocよりも高速なのですね。勉強いたします。
諸兄のご親切に重ねて御礼申し上げます。

貴重なお時間をありがとうございました。

osyo (manga osyo) wrote in #note-5:

やりたいこととしてはこういうことでしょうか?

def foo(&block)
  # ブロック引数がない場合は block は nil になる
  puts (block ? block : proc{"ブロックなし"}).call
end

foo #ブロックなし => "ブロックなし"
foo{"ブロックあり"} # ブロックあり => "ブロックあり"

matz (Yukihiro Matsumoto) wrote in #note-6:

「メソッド呼び出し時のブロック引数は省略可能にしておきたい」という要求の正確な意味がわからなかったのですが、これが「引数リストに&blk変数を指定したくない」というものだとすれば、それは3.0からできなくなりました。これまではprocメソッドを引数なしで呼び出す方法でメソッドに渡されたブロックをProcとして取り出せていましたが、暗黙のコンテキスト参照が入りますし、読解の点からも望ましくないと考えての変更です。「&blk引数」を使ってください。それに、たぶんこっちのほうが高速です。

Matz.

Actions #8

Updated by hsbt (Hiroshi SHIBATA) 8 months ago

  • Description updated (diff)
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0