読者です 読者をやめる 読者になる 読者になる

レガシーコード生産ガイド

私に教えられることなら

Scheme(Gauche)でSmalltalkライクな簡易OOPをしてみる

Smalltalk(Pharo)でLispを書いてみる - レガシーコード生産ガイド でメッセージング面白いな〜と思ったのでScheme(Gauche)で書いてみた。

(※追記 継承とかバグだらけだった。。)

なんかコード記法がおかしくなるのでgithubにpushした

github.com

何故Gaucheかと言うと、最終的にマクロは使わず関数のみで

gosh> (define v0 (<vec2d> :new))
v0
gosh> (v0 :set-x 1)
#<closure (make-instance receiver)>
gosh> ((v0 :set-y 2) :print)
(<vec2d> :x 1 :y 2)

gosh> (define v1 (<vec2d> :x 1 :y 5))
v1
gosh> (v1 :x)
1
gosh> (v1 :print)
(<vec2d> :x 1 :y 5)

gosh> (define v2 (<vec2d> :x 3 :y 9))
v2
gosh> ((v1 :add v2) :print)
(<vec2d> :x 4 :y 14)

gosh> (v2 :print)
(<vec2d> :x 3 :y 9)

こういう事がやりたかった。そのためにはLisp-1(funcallの必要が無い)で、かつキーワードが使えるGaucheを選んだ。素のSchemeってキーワード無いよね……?

要するに(method object arg1 arg2)の総称関数方式ではなく、(receiver message1 arg1 message2 arg2)Smalltalkライクに書いて、なおかつreceiverがマクロなどではなく単なる関数だったらイカすかな、と軽い気持ちで書き始めた。Clojureでマップがキーを取る関数として使える感覚に近い、かな。

使用風景

gosh> (define div (<division> :top 0 :bottom 9 :left 0 :right 9))
div
gosh> div
#<closure (make-instance receiver)>
gosh> (div :width)
10
gosh> (div :y)
0

gosh> (define tile1 (<tile> :type :wall :chara "壁"))
tile1

gosh> (define floor1 (<floor> :from-division div :filled-with tile1))
floor1

gosh> (floor1 :print-as-tiles)
壁壁壁壁壁壁壁壁壁壁
壁壁壁壁壁壁壁壁壁壁
壁壁壁壁壁壁壁壁壁壁
壁壁壁壁壁壁壁壁壁壁
壁壁壁壁壁壁壁壁壁壁
壁壁壁壁壁壁壁壁壁壁
壁壁壁壁壁壁壁壁壁壁
壁壁壁壁壁壁壁壁壁壁
壁壁壁壁壁壁壁壁壁壁
壁壁壁壁壁壁壁壁壁壁#<undef>

gosh> (((<vec2d> :x 2 :y 3) :set-x 4) :print) ; メソッドチェイン
(<vec2d> :x 4 :y 3)

実装の方法

:mes1 arg1 :mes2 arg2から:mes1:mes2というキーワードを作って、それでハッシュテーブルに突っ込んだ関数をarg1 arg2でapplyしてるだけ。インスタンススロットはクロージャで全てプライベートになるので、アクセサを作ってやる必要がある(個人的にはそのほうが好き)

継承は単一継承で、親にメッセージと引数のリストをそのまま投げる。ここでメッセージを再構成する無駄があるからどうにかしたい。

オブジェクトがルートで、それにクラスを作らせてるんだけど、自身もクラスオブジェクトにすることはできなかった。多分そのためにMetaObjectがあるんだろうな、とぼんやりわかった気がする。

感想

  • クラスを作るためのオブジェクトクラスが作るクラスインスタンスインスタンスを……って考えてたらそのまま行方不明になりそうになった。SmalltalkのMeta-Metaclassとかメタが重なってる物すんなり考えられる人すごいな……訓練が必要そうだ。
  • Pythonとかでself渡さなきゃいけない理由、この仕組みだからかな?
  • メソッドの宣言が(lambda () (let* ... (listの繰り返しで冗長になっている。ここでLispのマクロが活きてくるんだろう、書きたい。
  • 総称関数の方もやってみようかと思ったけど、予想以上に使えそうなものができたので、これを使って何かちょっと作ってみたくなった。やろう。
広告を非表示にする