“lethevert is a programmer - 言語を選ぶ基準”を Smalltalk で


一般的な Smalltalk の場合、

| f |
f := [:x |
   | g h |
   g := [:y | x + y].
   h := [:x | g value: 10].
   h].
(f value: 10) value: 1   "=> 20 "


残念ながら、Squeak Smalltalk ではこれでは駄目で、h のブロック変数名に x (すでに f のブロック変数として宣言されている…)は使えません。

| f |
f := [:x |
   | g h |
   g := [:y | x + y].
   h := [:x1 | g value: 10].
   h].
(f value: 10) value: 1   "=> 20 "


Ruby に直訳すると、こんなかんじ。

f = proc do |x|
  g = proc { |y| x + y }
  h = proc { |x1| g.call(10) }
  h
end
f.call(10).call(1)


また、Ruby で f のみメソッドにする版を、むりやり Squeak Smalltalk にて意訳すると…

self class compile: 'f: x
   | g h |
   g := [:y | x + y].
   h := [:x1 | g value: 10].
   ^h'.
(self f: 10) value: 1   "=> 20 "


さらに悪のりして、ブロックを使わず、メソッドのみで同じことを実現する版も。(^_^;)

self class compile: 'f: x
   self class compile: ''g: y ^x + y''.
   (self class >> #g: literalAt: 1) value: x.
   self class compile: ''h: x ^self g: 10''.
   ^self class >> #h:'.
(self f: 10) valueWithReceiver: self arguments: 1   "=> 20 "


self class が邪魔だなーと思いつつよく考えたら、これらのメソッドを起動するためのメッセージのレシーバは、self(トップレベルではたいていは nil …)である必要はないんですよね。Smalltalk のメッセージ式においては、通常の言語の第一オペランドや第一引数がレシーバに相当することを考慮すれば、各メソッドのパラメータ変数である x、y は、たとえば、Smalltalk では階乗を 10 factorial と書くようにレシーバ(それぞれのメソッド実行中のコンテキストでの self )として置き換え可能なので、もうすこしシンプル(?)にできそうです。

Integer compile: 'f
   Integer compile: ''g ^x + self''.
   (Integer >> #g literalAt: 1) value: self.
   Integer compile: ''h ^10 g''.
   ^Integer >> #h'.
10 f valueWithReceiver: 1 arguments: #()


#f が返してくる #h の起動も短く書けないかと、せっかく #h にアサインしているのでこれを活用し、定義したメソッド #h のオブジェクト本体の代わりにシンボル #h を返させて、

Integer compile: 'f
   Integer compile: ''g ^x + self''.
   (Integer >> #g literalAt: 1) value: self.
   Integer compile: ''h ^10 g''.
   ^#h'.
10 f value: 1


短くはなりますが、意味不明さは相変わらず。w むしろ BlockContext >> #value: と混同してさらに悪いことになっているかも。ちなみにここで使っている Symbol >> #value: の定義は、Squeak3.9 では、こんなふう。

Symbol >> value: obj
   ^obj perform: self