From: keiju@... (=?ISO-2022-JP?B?GyRCQFBETTc9PHkbKEI=?= ) Date: 1997-08-14T12:42:08+09:00 Subject: [ruby-dev:126] Re: meta programming features けいじゅ@日本ラショナルです. メイルが長くなってしまいました... In [ruby-dev :00122 ] the message: "[ruby-dev:122] Re: meta programming features ", on Aug/13 15:44(JST) matz@netlab.co.jp (Yukihiro Matsumoto) writes: >まつもと ゆきひろです 本題にはいる前に, 結構重要なことに気づきましたので, その話しから.... 実は, クラスは2種類のクラス変数を持てるんですね... * Foo::Var class Foo Var = ... end * Foo.type::Var class << Foo Var = ... end です. 同じ名前だと前者が優先されているみたいですね? # 何か話しが複雑になっているなあ... >|foo = Foo.new >|foo.eval "X = 1" >|1. 常に, Foo#Xを定義する. >|2. 常に, fooの特異クラスのXを定義する. >|3. Foo#Xがなければ Foo#Xの定義, 存在すればfooの特異クラスのXを定義する. >|4. 今のところ未定義 >|5. その他 > >4です.どうするのが良いと思いますか? >現行eval(private)では1ですね. ですね. >現行evalの基本は「そこにコードがそのままあった場合と同じ動き >をする」ことです.しかし,そもそもメソッド定義と定数定義には >存在できない場所がありますから,そういう場所でもevalの挙動は >任意に決められるわけです. >で,現状ではそのメソッドを定義している(定義を囲んでいる)クラ >スをメソッド,定数の定義先としているわけです. >で,考えられる変更点は 名前をつけますね. [定義A] > * evalをpublicにする [定義B] > * evalでのメソッドとクラスの定義先をクラスから特異クラスに > 変更する. >なんですよねえ. うーん. そうですねえ... まず, 元々の要求の元になったModule#evalの仕様はどうなっていたのか考え てみましょう. Foo.eval "Var = ..." -> Foo::Var Foo.eval "class Bar; ...; end" -> Foo::Bar Foo.eval "def foo; ...; end" -> Foo#foo Foo.eval "@baz = ..." -> Fooのインスタンス変数 これって, 上記のどれに属するかを考えるとどれにも属していないんですよね... これを[定義C]としましょう. また, FooをClassのインスタンスと見て, [定義A]を適用すると:: Foo.eval "Var = ..." -> Class::Var Foo.eval "class Bar; ...; end" -> Class::Bar Foo.eval "def foo; ...; end" -> Foo.foo Foo.eval "@baz = ..." -> Fooのインスタンス変数 とまりますよね? これはどう考えても使い物になりません. つまり, FooをClassのインスタンスと見て, [定義B]を適用すると;; Foo.eval "Var = ..." -> Foo.type::Var(Foo::Varではない!!) Foo.eval "class Bar; ...; end" -> Foo.type::Bar(Foo::Barではない!!) Foo.eval "def foo; ...; end" -> Foo.foo Foo.eval "@baz = ..." -> Fooのインスタンス変数 となりますよね? 良く良く考えると, Module#evalとObject#evalは求めている ことが全然違っているんですね... また, 逆に, [定義C]を一般のオブジェクトにそのまま適用すると:: foo.eval "Var = ..." -> foo::Var (定義不能) foo.eval "class Bar; ...; end" -> foo::Bar (定義不能) foo.eval "def foo; ...; end" -> foo#foo (定義不能) foo.eval "@baz = ..." -> fooのインスタンス変数 となります... 定義不能のところをどう解釈するかですが... それによって [定義A], [定義B]になるんですね. foo::VarをFoo::Varとみるか? Foo.type::Varと見るかによって... さらに, 考えてみますと: eval系列を以下のように並べます(typeは特異クラ スを返すとする). foo.eval [定義A] or [定義B] foo.type.eval [定義C] Foo.eval [定義C] Foo.type.eval [定義C] こうやって並べてみますと, foo.evalは[定義B]のほうが望ましいのでは? と いう気になってきますね. つまり, 定数定義 メソッド定義 インスタンス変数 foo.eval ? ? foo foo.type.eval foo.type::X foo.bar foo.type Foo.eval Foo::X Foo#bar Foo Foo.type.eval Foo.type::X Foo.bar Foo.type という感じになっていますので: 定数定義 メソッド定義 インスタンス変数 foo.eval foo.type::X foo.bar foo foo.type.eval foo.type::X foo.bar foo.type Foo.eval Foo::X Foo#bar Foo Foo.type.eval Foo.type::X Foo.bar Foo.type が一番美しいかなと... つまり, [定義B]ですね. で, Module#evalでは [定義C] さらに, >| eval "def foo; ...; end" > >それはいくらなんでも使いにくいんで,Object#evalの挙動を変更 >するなら,main.evalを再定義して,トップレベルではObjectクラ >スに対してメソッドや定数を定義するようにするでしょう,多分. ですね. main.eval() は 上記の定義にしてもらうというのはいかがでしょう か? XXX.evalの定義が3種類になってしまいましたが.... こんな感じでいかがでしょう? >|1. このまま. >|2. メソッド定義は定数定義と合わせる. >|3. 特異メソッドfooが定義されていれば, 特異メソッドの定義. 定義されてい >| なければFoo#fooの定義. 特異メソッドを明示的に定義したい時には, def >| obj.foo を用いる. >なんか3は動きが複雑だなあ.定数メソッドともクラスにするか特 >異クラスにするかどちらかで統一した方が良さそうに思います. ですかねえ.... 使う方も難しそうですねえ.... >|Object#typeは, 特異クラスを返した方がいいのか, あくまでもクラスを返し >|た方がいいのか... > >なんとなく特異クラスを返そうという気になっています. そうですか. それでは, さらに, MPF-APIのリクエストをします: * Module#singuler? そのクラスが, 特異モジュールかどうかの判断するメソッド. * Module#normal_class そのクラスが特異クラスだったら元のクラスを返す. normalクラスだったら 自分自身を返す. * Object#normal_type そのオブジェクトのnormalクラスを返す. これらは, 以下のように使い分けるために使います. foo.eval ... <- fooの特異クラスのアクセス foo.type.eval ... <- fooのクラス(特異クラスか普通のクラスか未定)のアクセス foo.normal_type ... <- fooのnormalクラスへのアクセス. ところで, 特異クラスが見えるようになると, そのサブクラスも作れるように なるんですかね? 現状ではどうかというと... class Foo def me "Foo" end end f = Foo.new class << f X = self def bar X end def me "f.type" end end X=f.bar class Baz < X end b = Baz.new print b.me うーん. 定義できるみたいですねえ... Foo > f.type > Baz # なんだか, 話しが複雑だなあ... __ ..........................................石塚 圭樹@日本ラショナル... ----------------------------------->> e-mail: keiju@bc.mbn.or.jp <<---