Ruby の ! について
はいどうも
! メソッド
Ruby における !
ってご存知ですか?
Ruby は演算子っぽいようなものもメソッドとして実装されているという話があったりします。
1 + 1 # Integer の + メソッドが呼ばれている class Integer # 上書き def +(other) 42 end end 1 + 1 # => 42
!
も BasicObject のメソッドとして定義されています。
/** * call-seq: * !obj -> true or false * * Boolean negate. *-- * \private *++ */ MJIT_FUNC_EXPORTED VALUE rb_obj_not(VALUE obj) { return RTEST(obj) ? Qfalse : Qtrue; } // ...略 rb_define_method(rb_cBasicObject, "!", rb_obj_not, 0);
class BasicObject def !(*) raise 'bang dayo-nn' end end !1 # Traceback (most recent call last): # 1: from basicobject.rb:7:in `<main>' # basicobject.rb:3:in `!': bang dayo-nn (RuntimeError)
ところで
クラス内でのメソッド呼び出しについては以下のようになりますよね。
class Foo def foo bar(1) # Foo#bar への呼び出しになる end def bar(arg) puts arg end end Foo.new.foo # => 1 が出力される
ということはこれで Foo#!
が呼ばれる気がするじゃないですか
class Foo def foo !(true) end def !(arg) puts '! called' false end end Foo.new.foo # なんもでない puts 'done'
でもそうはならないんですよね。
なんで?
parseされたときの結果を見ればわかりますが、 !
メソッドのレシーバーは nd_lit: 1
であることがわかります。
$ ruby --dump=parse -e '!(1)' ########################################################### ## Do NOT use this node dump for any purpose other than ## ## debug and research. Compatibility is not guaranteed. ## ########################################################### # @ NODE_SCOPE (line: 1, location: (1,0)-(1,4)) # +- nd_tbl: (empty) # +- nd_args: # | (null node) # +- nd_body: # @ NODE_OPCALL (line: 1, location: (1,0)-(1,4))* # +- nd_mid: :! # +- nd_recv: # | @ NODE_LIT (line: 1, location: (1,2)-(1,3))* # | +- nd_lit: 1 # +- nd_args: # (null node)
!(1)
は メソッド !
呼び出しの引数が 1
ではなく、(1)
(= 1
)への !
メソッド呼び出し扱いになるんですね。
parserの気持ちがわからない。
以上、昔この issue を見てよくわかんないなーと思って調べたことの dump です。
なおこの issue を理解するには更に if + regex literal のときの挙動を理解しておく必要があります。
https://docs.ruby-lang.org/ja/2.5.0/doc/spec=2fcontrol.html#if
補足
Ruby の動作は ruby 2.6.0preview1 (2018-02-24 trunk 62554) [x86_64-darwin17]
、
CRubyのソースコードは ac44ae58c697971e14e72493da2cfdd9a4e3b458 時点のものを基準としています。