[ruby-dev:37956] proposal: Module#method_adding

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

[ruby-dev:37956] proposal: Module#method_adding

SASADA Koichi
 ささだです.

 今,asakusa.rb で酒を飲んでたんですが,Module#method_adding とか,そう
いうメソッドというのはどうでしょうか. メソッドが定義される直前に呼ばれ
るメソッドです.

やりたいこと:
 例えば,foo というメソッドのオーバーライドをしたときに,old_foo のよう
な古いメソッドの alias を自動的に作るにはどうすれば,という話になりまし
た.Module#method_added を使うとどうだろう,と思ったのですが,これはすで
にメソッドを上書きした後でよばれるようです.

 ということで,メソッドを定義する直前にフックするためのメソッドとして
Module#method_adding というのを新設するのはどうだろう,という案がでまし
た.すでに,そういうことをする方法があればいいんですが,なければ,一つ検
討ご検討いただけないでしょうか.

--
// SASADA Koichi at atdot dot net

Reply | Threaded
Open this post in threaded view
|

[ruby-dev:37958] Re: proposal: Module#method_adding

Tadashi Saito
斎藤と申します。

On Tue, 10 Feb 2009 21:50:53 +0900
SASADA Koichi <[hidden email]> wrote:

> やりたいこと:
>  例えば,foo というメソッドのオーバーライドをしたときに,old_foo のよう
> な古いメソッドの alias を自動的に作るにはどうすれば,という話になりまし

old_fooというようなaliasを作る方法には問題があると思うので、それを推奨する
ような方向の変更には自分は反対です。

http://d.hatena.ne.jp/rubikitch/20080504/1209835550
http://blog.jayfields.com/2008/04/alternatives-for-redefining-methods.html

たとえばですが、fooというメソッドがold_fooとしてaliasされた後、fooがオーバーライド
されたとします。そのfooを含むスクリプトが他の人にも利用され、再度オーバーライドされたとき、
同じようにold_fooという名前でaliasが作成されない保証がどこにあるでしょうか。
old_という名前を付ける、という行為を複数の人間が思いつくのは、よくありそうなことです。

そのような時は、無限ループになってしまいます。

--
斎藤ただし

Reply | Threaded
Open this post in threaded view
|

[ruby-dev:37961] Re: proposal: Module#method_adding

SASADA Koichi
 ささだです.

Tadashi Saito wrote::

> old_fooというようなaliasを作る方法には問題があると思うので、それを推奨する
> ような方向の変更には自分は反対です。
>
> http://d.hatena.ne.jp/rubikitch/20080504/1209835550
> http://blog.jayfields.com/2008/04/alternatives-for-redefining-methods.html
>
> たとえばですが、fooというメソッドがold_fooとしてaliasされた後、fooがオーバーライド
> されたとします。そのfooを含むスクリプトが他の人にも利用され、再度オーバーライドされたとき、
> 同じようにold_fooという名前でaliasが作成されない保証がどこにあるでしょうか。
> old_という名前を付ける、という行為を複数の人間が思いつくのは、よくありそうなことです。
>
> そのような時は、無限ループになってしまいます。

 まぁ,単純な例を出しましたが,他にもオーバーライドしそうになったら例外
を発生させたい,とか.

# それこそ,old_foo を再度作ろうとしたときとか.
# あ,alias でもちゃんと method_added は呼ばれるんですね.

 old_foo については,名前の衝突しなさそうな方法は考えれば色々あるように
も思います.

 old_foo を安易に作ることはまずいかもしれませんが,提案自体は本質的には
それを助長することとは無関係に思えます.

# 出す例を間違えたかも

--
// SASADA Koichi at atdot dot net

Reply | Threaded
Open this post in threaded view
|

[ruby-dev:37966] Re: proposal: Module#method_adding

Tadashi Saito
斎藤です。

On Wed, 11 Feb 2009 03:32:41 +0900
SASADA Koichi <[hidden email]> wrote:

>  old_foo を安易に作ることはまずいかもしれませんが,提案自体は本質的には
> それを助長することとは無関係に思えます.

そうですね、だとしたら自分は反対する動機がないので、よいと思います。

ただやはり、

>  まぁ,単純な例を出しましたが,他にもオーバーライドしそうになったら例外
> を発生させたい,とか.

だけだとちょっと理由が弱い気もします。柔軟性が上がる一方で、黒魔術師の住み処を
一つ増やしてしまうだけのような気も。(でも、面白そうではあります)

自分はOOPに詳しくないのでよく分からないのですが、オーバーライドを積極的に禁止する
べき状況というのは、結構多くあるものだったり、最近流行ったりしているのでしょうか。

……とここまで書いて、本物のパチモンことなかださんの4年前の日記を思い出しました。
http://www.rubyist.net/~nobu/t/20051118.html

--
斎藤ただし

Reply | Threaded
Open this post in threaded view
|

[ruby-dev:37982] Re: proposal: Module#method_adding

SASADA Koichi
In reply to this post by SASADA Koichi
 ささだです.

SASADA Koichi wrote::

>  今,asakusa.rb で酒を飲んでたんですが,Module#method_adding とか,そう
> いうメソッドというのはどうでしょうか. メソッドが定義される直前に呼ばれ
> るメソッドです.
>
> やりたいこと:
>  例えば,foo というメソッドのオーバーライドをしたときに,old_foo のよう
> な古いメソッドの alias を自動的に作るにはどうすれば,という話になりまし
> た.Module#method_added を使うとどうだろう,と思ったのですが,これはすで
> にメソッドを上書きした後でよばれるようです.
>
>  ということで,メソッドを定義する直前にフックするためのメソッドとして
> Module#method_adding というのを新設するのはどうだろう,という案がでまし
> た.すでに,そういうことをする方法があればいいんですが,なければ,一つ検
> 討ご検討いただけないでしょうか.
>

 なんとなく実装してみました.ちなみに,method_added の rdoc がなかった
ので,singleton_method_added をコピーして適当に書きました.

 気づいた点は2点.

* ...ed 系のフックメソッドに,...ing 系を付け加えたくなってくる
  結構きりがない.hook 関数全般を set_trace_func みたいにまとめる?

* singleton_*ed があるメソッドは *ed の rdoc がない.


Index: object.c
===================================================================
--- object.c (リビジョン 22272)
+++ object.c (作業コピー)
@@ -523,6 +523,91 @@ rb_obj_tap(VALUE obj)
  */

 /*
+ * Document-method: method_adding
+ *
+ *  call-seq:
+ *     method_adding(symbol)
+ *
+ *  Invoked as a callback whenever a method (except singleton method)
+ *  is adding to the class.
+ *  This method is called before method adding process.
+ *
+ *     module Chatty
+ *       def Chatty.method_adding(id)
+ *         puts "adding: #{id}, respond_to?: #{method_defined? id}"
+ *       end
+ *       def Chatty.method_added(id)
+ *         puts "added: #{id}, respond_to?: #{method_defined? id}"
+ *       end
+ *       def self.one()     end
+ *       def two()          end
+ *       def Chatty.three() end
+ *     end
+ *
+ *  <em>produces:</em>
+ *
+ *     adding: two, respond_to?: false
+ *     added: two, respond_to?: true
+ *
+ */
+
+/*
+ * Document-method: method_added
+ *
+ *  call-seq:
+ *     method_added(symbol)
+ *
+ *  Invoked as a callback whenever a method (except singleton method)
+ *  is added to the class.
+ *
+ *     module Chatty
+ *       def Chatty.singleton_method_added(id)
+ *         puts "Adding #{id}"
+ *       end
+ *       def self.one()     end
+ *       def two()          end
+ *       def Chatty.three() end
+ *     end
+ *
+ *  <em>produces:</em>
+ *
+ *     Adding 2
+ *
+ */
+
+/*
+ * Document-method: singleton_method_adding
+ *
+ *  call-seq:
+ *     singleton_method_adding(symbol)
+ *
+ *  Invoked as a callback whenever a singleton method is adding to the
+ *  receiver.  This method is called before method added process.
+ *
+ *     module Chatty
+ *       def Chatty.singleton_method_adding(id)
+ *         puts "adding: #{id}, respond_to?: #{self.respond_to? id}"
+ *       end
+ *       def Chatty.singleton_method_added(id)
+ *         puts "added: #{id}, respond_to?: #{self.respond_to? id}"
+ *       end
+ *       def self.one()     end
+ *       def two()          end
+ *       def Chatty.three() end
+ *     end
+ *
+ *  <em>produces:</em>
+ *
+ *     adding: singleton_method_added, respond_to?: false
+ *     added: singleton_method_added, respond_to?: true
+ *     adding: one, respond_to?: false
+ *     added: one, respond_to?: true
+ *     adding: three, respond_to?: false
+ *     added: three, respond_to?: true
+ *
+ */
+
+/*
  * Document-method: singleton_method_added
  *
  *  call-seq:
@@ -533,7 +618,7 @@ rb_obj_tap(VALUE obj)
  *
  *     module Chatty
  *       def Chatty.singleton_method_added(id)
- *         puts "Adding #{id.id2name}"
+ *         puts "Adding #{id}"
  *       end
  *       def self.one()     end
  *       def two()          end
@@ -2488,6 +2573,7 @@ Init_Object(void)
     rb_define_method(rb_cBasicObject, "!=", rb_obj_not_equal, 1);

     rb_define_private_method(rb_cBasicObject, "singleton_method_added",
rb_obj_dummy, 1);
+    rb_define_private_method(rb_cBasicObject,
"singleton_method_adding", rb_obj_dummy, 1);
     rb_define_private_method(rb_cBasicObject,
"singleton_method_removed", rb_obj_dummy, 1);
     rb_define_private_method(rb_cBasicObject,
"singleton_method_undefined", rb_obj_dummy, 1);

@@ -2497,6 +2583,7 @@ Init_Object(void)
     rb_define_private_method(rb_cModule, "included", rb_obj_dummy, 1);
     rb_define_private_method(rb_cModule, "extended", rb_obj_dummy, 1);
     rb_define_private_method(rb_cModule, "method_added", rb_obj_dummy, 1);
+    rb_define_private_method(rb_cModule, "method_adding", rb_obj_dummy, 1);
     rb_define_private_method(rb_cModule, "method_removed",
rb_obj_dummy, 1);
     rb_define_private_method(rb_cModule, "method_undefined",
rb_obj_dummy, 1);

Index: vm_method.c
===================================================================
--- vm_method.c (リビジョン 22272)
+++ vm_method.c (作業コピー)
@@ -10,7 +10,7 @@ static void rb_vm_check_redefinition_opt

 static ID object_id;
 static ID removed, singleton_removed, undefined, singleton_undefined;
-static ID added, singleton_added;
+static ID added, singleton_added, adding, singleton_adding;

 struct cache_entry { /* method hash table. */
     ID mid; /* method's id */
@@ -125,6 +125,17 @@ rb_add_method(VALUE klass, ID mid, NODE
     if (OBJ_FROZEN(klass)) {
  rb_error_frozen("class/module");
     }
+
+    if (node && mid != ID_ALLOCATOR && ruby_running) {
+ if (FL_TEST(klass, FL_SINGLETON)) {
+    rb_funcall(rb_iv_get(klass, "__attached__"), singleton_adding, 1,
+       ID2SYM(mid));
+ }
+ else {
+    rb_funcall(klass, adding, 1, ID2SYM(mid));
+ }
+    }
+
     rb_clear_cache_by_id(mid);

     /*
@@ -749,6 +760,13 @@ rb_alias(VALUE klass, ID name, ID def)
  singleton = rb_iv_get(klass, "__attached__");
     }

+    if (singleton) {
+ rb_funcall(singleton, singleton_adding, 1, ID2SYM(name));
+    }
+    else {
+ rb_funcall(klass, adding, 1, ID2SYM(name));
+    }
+
     orig_fbody->nd_cnt++;

     if (st_lookup(RCLASS_M_TBL(klass), name, &data)) {
@@ -1132,7 +1150,9 @@ Init_eval_method(void)

     object_id = rb_intern("object_id");
     added = rb_intern("method_added");
+    adding = rb_intern("method_adding");
     singleton_added = rb_intern("singleton_method_added");
+    singleton_adding = rb_intern("singleton_method_adding");
     removed = rb_intern("method_removed");
     singleton_removed = rb_intern("singleton_method_removed");
     undefined = rb_intern("method_undefined");

--
// SASADA Koichi at atdot dot net

Reply | Threaded
Open this post in threaded view
|

[ruby-dev:37988] Re: proposal: Module#method_adding

Hidetoshi NAGAI
永井@知能.九工大です.

From: SASADA Koichi <[hidden email]>
Subject: [ruby-dev:37982] Re: proposal: Module#method_adding
Date: Fri, 13 Feb 2009 05:52:57 +0900
Message-ID: <[hidden email]>

> >  今,asakusa.rb で酒を飲んでたんですが,Module#method_adding とか,そう
> > いうメソッドというのはどうでしょうか. メソッドが定義される直前に呼ばれ
> > るメソッドです.
> >
> > やりたいこと:
> >  例えば,foo というメソッドのオーバーライドをしたときに,old_foo のよう
> > な古いメソッドの alias を自動的に作るにはどうすれば,という話になりまし
> > た.Module#method_added を使うとどうだろう,と思ったのですが,これはすで
> > にメソッドを上書きした後でよばれるようです.
> >
> >  ということで,メソッドを定義する直前にフックするためのメソッドとして
> > Module#method_adding というのを新設するのはどうだろう,という案がでまし
> > た.すでに,そういうことをする方法があればいいんですが,なければ,一つ検
> > 討ご検討いただけないでしょうか.
> >
>
>  なんとなく実装してみました.ちなみに,method_added の rdoc がなかった
> ので,singleton_method_added をコピーして適当に書きました.
>
>  気づいた点は2点.
>
> * ...ed 系のフックメソッドに,...ing 系を付け加えたくなってくる
>   結構きりがない.hook 関数全般を set_trace_func みたいにまとめる?
>
> * singleton_*ed があるメソッドは *ed の rdoc がない.

method_added を method_added(symbol, pre_method_obj, new_method_obj)
とするという考えはダメですかね?
そうすれば,define_method で旧い定義を別のメソッド名で定義したり,
適当に wrap して新旧織りまぜて呼び出すようにしたりとかが
自由に実装できそうに思います.
別名定義は自力でやるので,衝突チェックもその際にできるでしょう.

メソッドを書き換える前に呼ばれるか後に呼ばれるべきかによって,
pre_method_obj と new_method_obj とのいずれか一方は不要かもしれません.
Thread との絡みを考えると,メソッドを置き換えられてしまう前に
呼ばれるものが何かは欲しい気がしますね.
--
永井 秀利  ([hidden email])
九州工業大学 大学院情報工学研究院 知能情報工学研究系

Reply | Threaded
Open this post in threaded view
|

[ruby-dev:37989] Re: proposal: Module#method_adding

Urabe Shyouhei-4
Hidetoshi NAGAI さんは書きました:
> method_added を method_added(symbol, pre_method_obj, new_method_obj)
> とするという考えはダメですかね?

フックが呼ばれる度にMethodオブジェクトを生成するというのが著しく非効率という話
を聞きます。
method_addedが少々遅いくらいがどうした、というのも見識の一つとは思いますが。

Reply | Threaded
Open this post in threaded view
|

[ruby-dev:38001] Re: proposal: Module#method_adding

Hidetoshi NAGAI
永井@知能.九工大です.

From: Urabe Shyouhei <[hidden email]>
Subject: [ruby-dev:37989] Re: proposal: Module#method_adding
Date: Fri, 13 Feb 2009 12:19:50 +0900
Message-ID: <[hidden email]>
> フックが呼ばれる度にMethodオブジェクトを生成するというのが著しく非効率
> という話を聞きます。
> method_addedが少々遅いくらいがどうした、というのも見識の一つとは思いますが。

method_added が定義されていない限りは
Method オブジェクトは作らなくてもいいですよね.
Method の有無を確認した後で Method オブジェクトを作ればいいはずです.

method_added の有無を調べるステップはいずれにせよ一度は必要なので,
先に有無を調べておくこと自体はコストアップにはならないと思います.

method_added を本当に呼ぶことはさほど頻繁にはないでしょうから,
本当に呼ぶ必要があるときに少しくらい遅くても問題はないでしょう.

symbol だけを受け取る method_adding/method_added の場合には,
method_adding では定義される予定の method が得られず,
method_added では再定義前の method が得られません.
method_adding/method_added の両方での操作が必要になるかもしれません.
その場合,旧 method の Method オブジェクトを
method_adding から method_added に引きわたすための変数も
用意しなければならないかもしれません.
一つのメソッドで両方の Method オブジェクトを受け取れるなら,
ユーザは楽ができます.(^_^)

とはいえ,method 定義時に内部で
 (1) hook の有無を検査
 (2) hook が存在したなら,旧メソッドの Method オブジェクトを生成
 (3) method を定義
 (4) hook が存在したなら,新メソッドの Method オブジェクトを生成して
     hook の呼び出し
というステップを経なければいけないのは面倒なので,
method_adding と method_added とを組み合わせてなんとかできるなら
それで十分だろうという主張も理解できます.

私は Method オブジェクトを受け取れる方が嬉しいように思えますが,
「そうあるべき」とまで強くは主張しません.
判断はお任せします.

# ...って,無責任 (^_^;
--
永井 秀利  ([hidden email])
九州工業大学 大学院情報工学研究院 知能情報工学研究系