From: Akinori MUSHA Date: 2010-02-23T23:46:51+09:00 Subject: [ruby-dev:40463] Re: respond_to?() returns true --pgp-sign-Multipart_Tue_Feb_23_23:46:51_2010-1 Content-Type: text/plain; charset=ISO-2022-JP At Tue, 23 Feb 2010 14:09:52 +0900, matz wrote: > In message "Re: [ruby-dev:40461] respond_to?() returns true" > on Mon, 22 Feb 2010 23:49:37 +0900, "Akinori MUSHA" writes: > > | 今さらかもしれませんが、 respond_to? で protected メソッドを > |検査すると真になるのってそういうものでしたっけ。 > > protectedメソッドはレシーバによって呼べるか呼べないか決まるの > で、レシーバ情報を(通常の方法では)持たない respond_to? では > 「呼べるかもしれない」として真を返しています。  考えるに、 respond_to? で調べた上で、 protected であってもなお 呼びたいということはまれではないでしょうか。protected メソッドは その存在を知識として共有する相互関係において呼ばれるものであり、 同族判定は必要なら通常 duck type test に先だって行いますから、 respond_to? は protected メソッドを呼べるかもしれないとして気に する必要はないように思えます。 > | respond_to? を使う目的を考えると釈然としないのですが、この > |挙動は意図的でしょうか。 > > そういう意味では意図的です。深く考察したわけではありませんが。 > > 呼べる時だけにrespond_to?は真を返すべきである、と考えると、コー > ルフレームを走査して、レシーバ情報を取り出す必要があります。 > とすると、send経由で呼び出された時にはどうするか、など考える > とどんどん複雑化しそうです。 > > しかし、今、改めて考えると、method_missingで実現されるメソッ > ドでは、呼び出すことができても respond_to? は偽を返すわけです > から、これは「呼べないかもしれないものは偽」というルールであ > ると見なすことができます。ここからの類推からいえば protected > なメソッドに対するrespond_to? は偽を返すべきなのかもしれませ > ん。 > > どうしましょう?  もう一つ、 instance コンテキストで private メソッドについて respond_to? を呼んでも偽になります。 class X def foo p :foo end private :foo def bar # 事前に調べると偽 p respond_to?(:foo) # でも呼べる foo end end X.new.bar  このように respond_to? では、呼べるかどうかの検査という視点に おいて偽陽性と偽陰性がともに生じています。NoMethodError は気軽に rescue すべき例外ではなく(呼んだメソッドの先のバグも拾ってしまう)、 それを避けるための respond_to? ですから、少なくとも呼ぶこと自体は エラーにならないときのみ真を返すようにし、偽陽性を排除した方がいい のではないかと考えます。  ところで、この問題に気づいたのは OpenStruct をいじっていたときの ことです。同クラスには table という名前の protected メソッドがある にも関わらず、同じ名前のプロパティを使っても誤動作しなかったのです。  そのからくりはこうでした。未知のプロパティにアクセスされると、 method_missing は必要に応じて getter/setter を定義するのですが、 その「必要性」の検査は respond_to? で行っています。ここでたまたま respond_to?(:table) が真になるため、 obj.table = 1 としても getter メソッド table は(再)定義されません。従ってサブクラス等で protected メソッド table を使っていても問題ありません。一方、 obj.table と 値を取得しようとするときは protected メソッドの呼び出しは無効なので method_missing にルートされ、見事に値を得ることができるわけです。  本当は偶然ではないのかもしれませんが、特殊な例であることは確か なので、 method(key).owner から判定するような作りに直すことはでき ます(すでに手元にあります)。  respond_to? の仕様を変更するとこのような非互換性も生じますが、 呼べないものが真を返さなくなることは概して歓迎できるのではないかと 思います。何か見落としはあるでしょうか。 -- Akinori MUSHA / http://akinori.org/ --pgp-sign-Multipart_Tue_Feb_23_23:46:51_2010-1 Content-Type: application/pgp-signature Content-Transfer-Encoding: 7bit -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.10 (FreeBSD) iEYEABECAAYFAkuD6lsACgkQkgvvx5/Z4e7M2QCg0Vaak0F0xiGc3biFbBAoUbeQ bqEAnRrvOJ/iFR9SlZbwUHesqoo5QsfT =plRL -----END PGP SIGNATURE----- --pgp-sign-Multipart_Tue_Feb_23_23:46:51_2010-1--