[ruby-dev:40461] respond_to?(<protected method name>) returns true

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

[ruby-dev:40461] respond_to?(<protected method name>) returns true

Akinori MUSHA
 今さらかもしれませんが、 respond_to? で protected メソッドを
検査すると真になるのってそういうものでしたっけ。

 respond_to? を使う目的を考えると釈然としないのですが、この
挙動は意図的でしょうか。

% cat test.rb
class X
  def foo
    p :foo
  end
  protected :foo
end

x = X.new

# respond_to? で事前にテストするとtrue
p x.respond_to?(:foo)
#=> true

# でも実際に呼ぶとNoMethodError
x.foo
#=> protected method `foo' called for #<X:0x00000100861ae0> (NoMethodError)

--
Akinori MUSHA / http://akinori.org/

attachment0 (203 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

[ruby-dev:40462] Re: respond_to?(<protected method name>) returns true

Yukihiro Matsumoto
まつもと ゆきひろです

In message "Re: [ruby-dev:40461] respond_to?(<protected method name>) returns true"
    on Mon, 22 Feb 2010 23:49:37 +0900, "Akinori MUSHA" <[hidden email]> writes:

| 今さらかもしれませんが、 respond_to? で protected メソッドを
|検査すると真になるのってそういうものでしたっけ。

protectedメソッドはレシーバによって呼べるか呼べないか決まるの
で、レシーバ情報を(通常の方法では)持たない respond_to? では
「呼べるかもしれない」として真を返しています。

| respond_to? を使う目的を考えると釈然としないのですが、この
|挙動は意図的でしょうか。

そういう意味では意図的です。深く考察したわけではありませんが。

呼べる時だけにrespond_to?は真を返すべきである、と考えると、コー
ルフレームを走査して、レシーバ情報を取り出す必要があります。
とすると、send経由で呼び出された時にはどうするか、など考える
とどんどん複雑化しそうです。

しかし、今、改めて考えると、method_missingで実現されるメソッ
ドでは、呼び出すことができても respond_to? は偽を返すわけです
から、これは「呼べないかもしれないものは偽」というルールであ
ると見なすことができます。ここからの類推からいえば protected
なメソッドに対するrespond_to? は偽を返すべきなのかもしれませ
ん。

どうしましょう?

                                まつもと ゆきひろ /:|)

Reply | Threaded
Open this post in threaded view
|

[ruby-dev:40463] Re: respond_to?(<protected method name>) returns true

Akinori MUSHA
At Tue, 23 Feb 2010 14:09:52 +0900,
matz wrote:
> In message "Re: [ruby-dev:40461] respond_to?(<protected method name>) returns true"
>     on Mon, 22 Feb 2010 23:49:37 +0900, "Akinori MUSHA" <[hidden email]> 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/

attachment0 (203 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

[ruby-dev:40464] Re: respond_to?(<protected method name>) returns true

Yukihiro Matsumoto
まつもと ゆきひろです

In message "Re: [ruby-dev:40463] Re: respond_to?(<protected method name>) returns true"
    on Tue, 23 Feb 2010 23:46:51 +0900, "Akinori MUSHA" <[hidden email]> writes:

| もう一つ、 instance コンテキストで private メソッドについて
|respond_to? を呼んでも偽になります。

これは仕様です。まず、原則として、instanceコンテキスト呼び出
しされたかどうかをメソッドの実装側が知ることは困難ですから、
respond_to? は、それに対する対応は行いません。よって、

  self.respond_to?(:foo)



  respond_to?(:foo)

の意味は同じです。そして、respond_to? は private なものを含
むかどうかは、第二引数で指定します。trueが指定された時は、
privateを含みます。

| このように respond_to? では、呼べるかどうかの検査という視点に
|おいて偽陽性と偽陰性がともに生じています。NoMethodError は気軽に
|rescue すべき例外ではなく(呼んだメソッドの先のバグも拾ってしまう)、
|それを避けるための respond_to? ですから、少なくとも呼ぶこと自体は
|エラーにならないときのみ真を返すようにし、偽陽性を排除した方がいい
|のではないかと考えます。

というわけで、privateの件は偽陰性ではないと考えます。ただし、
前のメールでも書いたように method_missing による偽陰性は存在
するので、本質的には指摘は当たっているのですが。で、偽陰性の
排除についてですが、ちょっと考えた結果、賛成します。

| respond_to? の仕様を変更するとこのような非互換性も生じますが、
|呼べないものが真を返さなくなることは概して歓迎できるのではないかと
|思います。何か見落としはあるでしょうか。

私は思いつかないのですが、どなたか気がつきますでしょうか。
移行プロセスにはリリースマネージャに一任します。

                                まつもと ゆきひろ /:|)

Reply | Threaded
Open this post in threaded view
|

[ruby-dev:40465] Re: respond_to?(<protected method name>) returns true

Akinori MUSHA
At Wed, 24 Feb 2010 00:55:39 +0900,
matz wrote:

> | このように respond_to? では、呼べるかどうかの検査という視点に
> |おいて偽陽性と偽陰性がともに生じています。NoMethodError は気軽に
> |rescue すべき例外ではなく(呼んだメソッドの先のバグも拾ってしまう)、
> |それを避けるための respond_to? ですから、少なくとも呼ぶこと自体は
> |エラーにならないときのみ真を返すようにし、偽陽性を排除した方がいい
> |のではないかと考えます。
>
> というわけで、privateの件は偽陰性ではないと考えます。ただし、
> 前のメールでも書いたように method_missing による偽陰性は存在
> するので、本質的には指摘は当たっているのですが。で、偽陰性の
> 排除についてですが、ちょっと考えた結果、賛成します。
 ご検討ありがとうございます。賛成くださるのは偽陽性の排除ですよね。

 偽陰性の方は、ちょっと私のニュアンスがよくなかったですが、仕様と
して受け入れています。両側がぶれるのはよろしくないので揃えるべき
方だけでも揃えましょうという提案でした。

> | respond_to? の仕様を変更するとこのような非互換性も生じますが、
> |呼べないものが真を返さなくなることは概して歓迎できるのではないかと
> |思います。何か見落としはあるでしょうか。
>
> 私は思いつかないのですが、どなたか気がつきますでしょうか。
> 移行プロセスにはリリースマネージャに一任します。

 私は広義にはバグといってもいいような気がしますし、 grep しても
protected メソッドの使用頻度はさほど多くないので大きな問題には
ならないと推測します。とはいえ 1.8 は変更せずそのままとします。

--
Akinori MUSHA / http://akinori.org/

attachment0 (203 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

[ruby-dev:40466] Re: respond_to?(<protected method name>) returns true

Yukihiro Matsumoto
まつもと ゆきひろです

In message "Re: [ruby-dev:40465] Re: respond_to?(<protected method name>) returns true"
    on Wed, 24 Feb 2010 01:12:55 +0900, "Akinori MUSHA" <[hidden email]> writes:

|> 前のメールでも書いたように method_missing による偽陰性は存在
|> するので、本質的には指摘は当たっているのですが。で、偽陰性の
|> 排除についてですが、ちょっと考えた結果、賛成します。
|
| ご検討ありがとうございます。賛成くださるのは偽陽性の排除ですよね。

あ、そうです。protectedに対してrespond_to?が真を返しているの
を偽に統一するということですね。