原です。

久しぶりに提案です。型キャストの話です。

Ruby で数学などしていると、やたらクラスばかり作ってしまって、何がなん
だか分からなくなることがあります。例えば、配列はそれをベクトルと見たり、
順列と見たり、集合と見たりするわけで、それぞれの「見なし」のためには面
倒でもそれぞれ Array に delegate するクラスを作り、その都度オブジェク
トを new で作るのが正式な方法です。しかし、昔は組み込みクラスはインス
タンス変数を持てなかったのでしかたが無かったのですが、今では delegate 
ではなく、直接 Array を継承するのもオーライかと思います。そこで、型変
換の仕組みを考えてみました。

ところで、実は既に型変換のようなものは備わっていて、例えば Array::[] 
があります。仮に Array の要素の総和を与える sum を定義することを考えま
しょう。ここで Array に組み込んでしまうのはシステム全体に影響があるの
で、サブクラスにします。

class Array2 < Array
   def sum
     tot = 0
     each do |x| tot += x; end
     tot
   end
end

そすると、Array はうまいことできていて Array2::[] が Array::[] を
継承してうまく動いてくれます。だからこんな風に使えます。

(各行の和を求めるスクリプト)
while line = gets
   puts( Array2[*line.split.collect{|x| Integer(x)}].sum )
end

これは Array2[] がキャストを行ってくれているのだと見ることができます。
これに似たキャストの仕組みを全てのクラスに持たせようというわけです。

で、キャストの形式ですが、Array2**obj のようにしようかと思います。本当
は [] を使いたいところですが、既に使われいるので。しかも、** は結合力
が強く、かつ、右結合なのもいいところです。これを使うと上はこんな風に書
けます。

while line = gets
   puts( (Array2**line.split.collect{|x| Integer(x)}).sum )
end

さて、これを行う仕組みですがいたってシンプルです。キャストをしたいクラ
スには、メソッド casting を用意します。casting は基本的には配列を返し、
クラス Foo のインスタンス foo に対して

   Foo.new(* foo.casting)

が foo.dup と同じ効果をもつように定義します。そして Class では、キャスト
を casting を使って行うわけです。次が Ruby で書いた例です。

class Class
   def **(other)
     new(* other.casting)
   end
end

一般のクラスでキャストしたい(されたい?)クラスでは次のように定義して
おくといいでしょう。

class Foo
   def initialize(a, b, c)
     @a, @b, @c = a, b, c
   end
   def casting
     [@a, @b, @c]
   end
end

基本は上の通りなのですが、組み込みクラス Array の new は特殊な意味があ
るので、** はこのままではうまくありません。また、効率を考えると、多少
統一性が失われても、組み込みクラスに対しては余計な配列オブジェクトを作
らないように工夫しておいた方がいいでしょう。例えば、

class Class
   def **(other)
     if self <= Array or self <= Hash
       self[*other.casting]
     elsif self <= String
       new(other.casting)
     else
       new(*other.casting)
     end
   end
end

class String
   def casting; self; end
end
class Array
   def casting; self; end
end
class Hash
   def casting; to_a.flatten; end
end
class Range
   def casting
     [first, last, exclude_end?]
   end
end

みたいに。

で、提案なんですが、この Class#** と「組み込みクラスの」casting を組
み込みで用意したらどうかということです。

今見たように Ruby で書けるなら組み込みにする必要ないではないか、という
意見もありそうですが、TCPSocket.casting なんか自分で書くのちょっと面倒
ですよね。

また、これは余計かもしれませんが、次の Class#castable というのもあると
便利かもしれません。Ruby で書くと

class Class
   private
   def castable
     eval "class Object; def #{name}; #{name}**self; end; end",
       TOPLEVEL_BINDING
   end
end

みたいな感じで、

class Foo
   castable
end

とすると、Object#Foo が Foo**(obj) をするようになるというものです。
(ただし先の定義例では castable は継承できません。)


ご意見いかがでしょう。こういうのがあると便利だと思いませんか?

class String2 < String
   castable
   def each(&b); each_byte(&b); end
end
"ABCDE".String2.collect{|x| x - ' '[0]} #=> [33, 34, 35, 36, 37]

class Hash
   castable
end
%w{abc, 0, efg, 1}.Hash #=> {"abc" => "0", "efg" => "1"}


最後にもう一度、キャストの仕組みを Ruby で書きかけたものを添えておきま
す。

-----^ cast.rb
class Class
   def **(other)
     if self <= Array or self <= Hash
       self[*other.casting]
     elsif self <= String
       new(other.casting)
     else
       new(*other.casting)
     end
   end
end

class String
   def casting; self; end
end
class Array
   def casting; self; end
end
class Hash
   def casting; to_a.flatten; end
end
class Range
   def casting; [first, last, exclude_end?]; end
end

##### TEST #####
class String2 < String
   def each(&b); each_byte(&b); end
end
p( (String2**"ABCDE").collect{|x| x - ' '[0]} ) #=> [33, 34, 35, 36, 37]

class Array2 < Array
   def sum
     tot = 0
     each do |x| tot += x; end
     tot
   end
end
p( (Array2**[1, 2, 3, 4, 5]).sum ) #=> 15

class Class
   private
   def castable
     eval "class Object; def #{name}; #{name}**self; end; end",
       TOPLEVEL_BINDING
   end
end

class String2 < String
   castable
end
p "ABCDE".String2.collect{|x| x - ' '[0]} #=> [33, 34, 35, 36, 37]

class Array2 < Array
   castable
end
p [1, 2, 3, 4, 5].Array2.sum #=> 15

class Hash
   castable
end
p %w{abc 0 efg 1}.Hash #=> {"abc" => "0", "efg" => "1"}
-----$ cast.rb