ごとけんです

In message "[ruby-list:18684] Re: paramclass"
    on 99/11/17, Shin-ichiro Hara <sinara / blade.nagaokaut.ac.jp> writes:

>なるほど、すると a == b ならば Pclass(a) == Pclass(b) であってほしい訳ですね。

その通りです。とくに foo = Pclass(a,b,c).new のとき
  
  foo.is_a? Pclass(a,b,c)

であって欲しいですね。さらに

  foo.is_a? Pcalss(a)

でもあって欲しい気もちょっとしますが、意味ないかな。

>単に append_features の「特異」版として考えたのです。include する所を
>extend したらどうなるかなあと。

なーる。

>                               でも extend_object を「特異的に使えば
>いい」のだと気づきました。(^^;
>こんな感じで。

うわあっ、すごい。僕にはこんなの書けません。理解するのにも、
しばらくかかりました ^^;;

ところで、インターフェイスについて考え直してみると、

>class Foo
>end
>Foo.extend ClassParameterizer

こんな感じで、そとから任意のクラスをパラメトライズ宣言できる
のには、なにか違和感を感じるようになりました。デフォルトのパ
ラメータは必須でなくても良いですが(抽象クラスみたいなかんじ)、
パラメータの意味はなにかしら明記したいこともあって、クラス定
義側でincludeするというのは変えたくない気がします。

TOPLEVEL_BINDING で eval するというアイデアだけを頂いて書き
直しました。最後につけます。例として、[ruby-list:18715]の固
定サイズな配列をパラメトリッククラスとして書いてみました。
FixedSizeArray(size, default) でクラスをあらわすモノです。

require "paramclass"

class FixedSizeArray
  include Parametric
  include Enumerable

  def size;           parameters[0];   end
  def default_value;  parameters[1];   end

  def self.[](*args); self.new(*args); end

  def initialize(*args)
    @ary = args
    s = @ary.size
    raise ArgumentError, "Too many initial values" if s > size
    @ary.fill(default_value, s, size - s)
  end

  def [](n)
    if n < -size or size <= n
      raise IndexError, "index out of range"
    end
    @ary[n]
  end

  def []=(n,v)
    if n < -size or size <= n
      raise IndexError, "index out of range"
    end
    ary[n] = v
  end

  def each(&blk)
    @ary.each(&blk)
  end

  def inspect
    @ary.inspect
  end
end

# example

p a = FixedSizeArray(5, "")["foo", "bar", "baz"]
  #=> ["foo", "bar", "baz", "", ""]
p a.is_a? FixedSizeArray(5, "")
  #=> true

# # paramclass.rb # # begin: class variable emuration by Matz [ruby-dev:8245] class Module private def module_attr(*names) names.each do |name| name = name.id2name unless name.kind_of? String self.module_eval <<-EOS CV_#{name} = [] class <<self def #{name}; CV_#{name}[0] end def #{name}=(val); CV_#{name}[0] = val end end EOS end end end class Class private alias class_attr module_attr end # end: class variable emuration by Matz [ruby-dev:8245] module Parametric def Parametric.append_features(mod) super mod # class generating function cn = mod.name eval "def #{cn}(*args); #{cn}.instance_eval{class_generate(*args)} end", TOPLEVEL_BINDING mod.module_eval{ class_attr :rootclass, :class_parameters, :classes @rootclass = true } mod.rootclass = mod mod.class_parameters = {} mod.classes = {nil => mod} def mod.class_generate(*args) args = nil if args.empty? klass = self::classes[args] || self::classes[args] = Class.new(self) klass.class_parameters[klass] = args unless klass.rootclass? klass end def mod.parameters if rootclass? defined?(default_parameters) ? default_parameters : [] else res = nil self.ancestors.find{|i| res = class_parameters[i] } res end end def mod.rootclass? @rootclass end end def parameters self.type.parameters end end ########## devel test ########## if __FILE__ == $0 def xmp(arg, show = true) # prints expample code and result line by line if show __res__ = [] eval arg.gsub(/^(.*)\n?/){ "__res__ << (#{$1}).inspect;" } arg.split(/\n/).each_with_index{|l,i| (puts "\n" ; next) if l =~ /^$/ print "#{l}\n #=> #{__res__[i]}\n" } else print arg; eval arg end end xmp <<-EOS, nil class C include Parametric def C.default_parameters [100] end end EOS xmp <<-EOS C.parameters C(10).parameters C(10).id == C(10).id C(20,10) < C C.rootclass C(10).rootclass C.rootclass? C().rootclass? C("foo").rootclass? D = C(0) C(0) EOS xmp <<-EOS, nil class Bar < D; include Parametric def Bar.default_parameters superclass.parameters + [""] end end EOS xmp <<-EOS Bar.rootclass Bar.parameters Bar.new.is_a? D Bar.new.is_a? C(0) Bar.new.is_a? C(10) EOS end