GNU Smalltalk の Traits をいじくり中(やや挫折ぎみ)

メモ。

前回のSmalltalk勉強会のテーマは Traits でした。ふみー、Traits、興味津々ですなぁ。ヨダレガデマス! いくべきだったかも。

勉強会の案内を観ると、

Squeak 3.9で採用され、現在はPharoやGNU Smalltalkにも取り入れられています。Squeak 4.1では軽量版が再実装されました。

smalltalk-users.jp - 第21回Smalltalk勉強会

と 最近マイブームな GNU Smalltalk でも使えるのですか!ならば早速 GNU Smalltalk で遊んでみようと、3.2 のデフォルトを Browser で舐め回しても Traits の トの字もありません。うにゃ?

で、Google先生に聞いて見たところ github に gst-traits なるものが転がっているのを発見。

http://github.com/MrGwen/gst-traits

これのことかな? gst 3.2 では Traits はパッケージとしては用意されているけれど、まだクラスライブラリがこれを利用しているとかは無い という理解でOKかしら。

そんなわけで、GNU Smalltalk で Traits のお勉強をしてみました。


* * *


まずはなにはなくとも、パッケージインストール。

$ git clone http://github.com/MrGwen/gst-traits.git
$ cd gst-traits/
$ ./package.sh 

git で取ってきて package.sh を実行するだけなんて簡単過ぎます。へみへみ。

早速 gst-browser で見てみると、Traits パッケージに いろいろ入ってます。

また既存クラスも拡張されていて、たとえば 既存クラスの Behavior (Class や Metaclass の スーパークラス ClassDefinition のスーパークラス)に #uses: メソッドが増えてたりなどなど。

こういう風に、既存クラスにガンガンメソッドを追加できちゃうし、それがごく普通に行っていい流儀なのが Smalltalk の面白さなのだけれど、単なるコードの差分マージなので、単にパッケージを使うだけでも いろいろぶつかったときとか大変なのも Smalltalk. そういう意味で Squeakワナビなわたしにはある意味地獄でした。

そんな現状をもっとモジュールとして何とかしましょうよ・・というのが Traits ・・という理解でよいのかしら。ふみみ。まぁ、そこらへんは勉強してのお楽しみ。

かるく Traits の 実装をみると、

Behavior >> uses: aTrait [
    <category: 'traits'>

    methodDictionary ifNil: [ methodDictionary := MethodDictionary new ].
    aTrait installOn: self
]
Trait >> installOn: aBehavior [
    <category: 'instaling'>

    self uses installOn: aBehavior.
        self methodDictionary do: [ :each | | cp |
            aBehavior methodDictionary add: (each selector -> (cp := each withNewMethodClass: aBehavior)).
            cp recompile ]
]

みたいに methodDictionary にガンガン Trait の辞書を追加していく感じ。ふみみ、「トレイトはクラスのメソッドホルダとしての機能のみに特化されていて、たんなるメソッドのコレクションにすぎない」と聞いていたまんまの実装みたい。

こういう、言語や環境の仕組み部分も シームレスにSmalltalk 自身で観られるのも、Smalltalk の嬉しいところ(やっててよかった Smalltalker)。


* * *


そんなこんなで、かるーくブラウズもしてみたことですし、早速 Traits を使ってコーディング。まずはノリで、「こんなの通るといいな」と書いてみたコード。

PackageLoader fileInPackage: 'Traits'.

Traits.Trait named: FooTrait [
    say [
        'Foo' printNl.
    ]
]

Object subclass: Bar uses: FooTrait [
    print [
        ^'Bar'
    ]
]

Eval [
    | obj |
    obj := Bar new.
    obj print printNl.
    obj say
]
$ gst try1.st
"Global garbage collection... done"
Loading package Traits
try1.st:3: expected Eval, Namespace or class definition
try1.st:7: expected expression

案の定通りません。そもそも GNU Smalltalkスクリプトスタイルのところでエラーになります(Traits が書けない / 同様に Object subclass:... に uses: を含めたものもない)

ま、これは仕方ない。なのでちょっとコードを差し替えてみたのですが、

PackageLoader fileInPackage: 'Traits'.

Object subclass: Bar [
    print [
        ^'Bar'
    ]
]

Eval [
    | obj |

    (Traits.Trait named: #FooTrait uses: {})
        compile: 'say [ ''foo'' printNl ]'.

    Bar uses: FooTrait.

    obj := Bar new.
    obj print printNl.
    obj say
]

これもダメ。#FooTrait という名前にTrait が入って無い様子。ふみみ、Trait class >> named: を読んでみると、確かに単にTrait のインスタンスをつくって返しているだけみたいですね。

やっぱり適当にそれっぽいコードを書いて Try & Error はよくないなー、ということで、gst-traits のユニットテストを参考にコードを組んでみました。

PackageLoader fileInPackage: 'Traits'.

Object subclass: Bar [
    print [
        ^'Bar'
    ]
]

Eval [
    | obj t1 |

    t1 := (Traits.Trait named: #FooTrait uses: {})
        compile: 'say [ ''foo'' printNl ]';
        yourself.

    Bar uses: t1.

    obj := Bar new.
    obj print printNl.
    obj say
]
s$ gst try3.st
"Global garbage collection... done"
Loading package Traits
Recompiling selector: #say
'Bar'
'foo'

さすがに今度はちゃんと動いたけれど、うーん、これ本当かしら。

これでは「Smalltalk に Traits を追加した」とは言い難いというか、まだ「Traits の機能を Smalltalk で書いてみた」以上のものじゃないような。Traits を使ってプログラミングしてみよう・・というにはあまりにも アレです。ツールや文法のサポートは欲しいなー。

ご本人も「This is a young version」とおっしゃってるし、まだ使って遊べる段階じゃないのかな。

けれども、なんだかわたしが激しく使い方を間違っている気もするのよね。うーむー。なので、猛烈に Example が欲しいのですが、・・・情弱ゆえに見つかりませぬ...しくしく。


・・・というところで今週はおしまい。全然 Traits で遊べなかったですヨー。素直に Squeak で勉強すればよかったデス orz