From: keiju@... (Keiju ISHITSUKA) Date: 1998-03-12T14:43:13+09:00 Subject: [ruby-dev:1718] new coerce [Re: coerce] けいじゅ@日本ラショナルソフトウェアです. 新coerce案を検討します. In [ruby-list :07032 ] the message: "[ruby-list:7032] Re: coerce ", on Mar/10 02:18(JST) Keiju ISHITSUKA writes: >>(2) 本当は Numericクラスあたりでやらしたいことを >> わざわざ Scalarクラスのオブジェクトを作って >> それにやらしている。 >> >> ==> 適用すべきメソッドを coerce の返値で教えて >> やってはどうか (中略) >こうやって考えると, この方法は, 簡単な割に結構強力でかつ, 過去との互換 >性も大体あるといえます(引数が1つ増えるだけ). > >というわけで, coerceの > > self.coerce(other, op) -> (other', self', op') > >への仕様変更を私も推したいと思います(^^;;; > >私はなっとくしたのですが, 松本氏が納得しないことには意味がなくて... い >かがでしょう? >> matz 一言でいいますと, 新coerce[新案1]はなかなか良いアイディアだと思います. 1. MatrixにおけるScalarの様な間接クラスは必要なくなる(と思う). 2. 間接クラスに変わる方法は, ダブルディスパッチに近い部分があり分かり やすくなる(と思う). 問題もあります. 3. coerceが大きくなる. でも, それは元々複雑な場合で, 現coerceでScalarの様な間接クラスを導 入しなくてはいけない場合だけだと思います. ほとんどの場合第3引数は無 視できます. 4. このままでは, coerceは良いが, coerceを呼んだ側が面倒. この問題を豊福@パパイヤさんの例を引いて検討しますと: class Fixnum def *(other) case other ... else x, y, meth = other.coerce(self, :*) if (meth) x.send(meth, y) else x * y end end end end というようなコードを書かなくてなならなくて, 常に演算子を考えなくてはな らないのが面倒ですよね. そこで, その問題を解消できる[新案2]を提出します. [新案2] 呼出側では直接coerceを呼ぶのではなくて演算を代わりにやってくれる以下の ような別メソッドを導入し: class Object def delegate_op(other, op) x, y, op2 = coerce(other, op) if op2 x.send(op2, y) else x.send(op, y) end end end と定義して, class Fixnum def *(other) case other ... else other.delegate_op(self, :*) end end end と呼び出せるようにするのはいかがでしょう? そうすると, 呼出側も現coerceよりも簡単になりますし, 第一間違いが少なく なる(^^;;; この案でちょっと気になるのは, elegate_opがObject/Kernelにないと困るっ てことですかね... [新案3] もう1つの案は, coerceをやめてdelegete_opだけにしてこっちを再定義するよ うにするというのもです. そうすると, 以下のようなものが: > class Matrix > def coerce(other, meth) > case meth > when :+ > case other > when Numeric > エラー > ... > end > when :* > case other > when Numeric >--> return self, other, :r_mul > ... > end > ... > end > end > end このように, returnしていたものが class Matrix def coerce(other, meth) case meth when :+ case other when Numeric エラー ... end when :* case other when Numeric --> self.r_mul(other) ... end ... end end end のように直接呼び出せるようになります. でも, Matrixみたいな場合はそうでも, 以下のような単純な場合は class Complex def coerce(other, op) if Complex.generic?(other) return Complex.new(other), self else super end end end で済むのに class Complex def coerce(other, op) if Complex.generic?(other) Complex.new(other).send(op, self) else super end end end と書かなくてはならなくなって, かえって面倒ですね... というわけで[新案2]ぐらいが妥当かと思うのですが, いかがでしょう? [新案2]では, 1. 現coerceよりも若干呼出側が簡単になる. 2. coerceの記述は, 簡単な場合は昔とほとんど同じ(引数が増えるだけ). 3. 複雑な場合は, 間接的なクラスが必要なくなり, 記述は楽になる. 問題は, (特に呼出側において)過去との互換性がない. というのがありますが, [新案1]でもそれは代わりません. 互換性については, これを使っている人はruby内部と私が作ったモジュールだ けだと思いますので(多分ですが(^^;;;), 手間はかかりますが互換性に関して はあまり気にしなくて良いと思います. __ ................................石塚 圭樹@日本ラショナルソフトェア... ----------------------------------->> e-mail: keiju@rational.com <<---