EQL スペシャライザで猫と犬をしゃべらせる

メタクラスを使うのは、けっこう普通にできそうな気がしたので、EQL スペシャライザで。ただ、Gauche にはこの機能はないので、CLISP を使いました。

(setq <cat> (defclass <cat> () ()))
(setq <dog> (defclass <dog> () ()))
(defmethod print-object ((self (eql <cat>)) stream) (princ "meow!" stream))
(defmethod print-object ((self (eql <dog>)) stream) (princ "woof!" stream))
(prin1 (list <cat> <dog>))
;;=> (meow! woof!)

speak のコールすらいらなくするために、print-object のオーバーロードSmalltalk でいうところの #printOn: のオーバーライド)というこそくな手を使っています。w


Ruby とか Smalltalk だとクラスがメタクラスのソルインスタンスなので、上と同じことをしようとすると、結局、Gaucheメタクラス版との違いがなくなってしまうようです(EQL スペシャライザを意識して作られた「特異メソッド」という機能を使うことになる Ruby ならなおさら)。

ただ、Gauche(や CLOS)と違い、RubySmalltalk ではクラスを作ると自動的にメタクラスも作られる(Lisper に言わせると無駄に作られてしまう?)ので、結果、ワンステップ減らせます。

Ruby
class Cat
  def self.to_s; "meow!" end
end

class Dog
  def self.to_s; "woof!" end
end

p [Cat, Dog]
#=> [meow!, woof!]
Smalltalk
Object subclass: #猫

猫 class >> printOn: stream
    stream nextPutAll: 'ミャウ!'
Object subclass: #犬

犬 class >> printOn: stream
    stream nextPutAll: 'ワン!'
{猫. 犬}   "=> {ミャウ! . ワン!} "

たしか Pythonメタクラスを使えるはず…と思い出したので、調べつつ似たようなのを書いてみました。

Python
class metaCat(type):
  def __repr__(self): return "meow!"

class metaDog(type):
  def __repr__(self): return "woof!"

class Cat:
  __metaclass__ = metaCat

class Dog:
  __metaclass__ = metaDog

print [Cat, Dog]
#=> [meow!, woof!]