#:g1: Spice Lisp: Flavorsの紹介

Posted 2014-08-24 15:50:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の237日目です。

Spice Lisp: Flavorsとはなにか

 Spice Lisp: Flavorsは、Steven Handerson氏作のSpice Lisp上のFlavorsの実装です。

パッケージ情報

パッケージ名Spice Lisp: Flavors
Quicklisp×

インストール方法

 Rutgers Common Lisp(TOPS-20 Common Lisp)のアーカイブの中にSpice Lispプロジェクトが配布していたFlavorsのファイルが埋もれていますので、適当に動かします。

 例のごとく、ANSI CLで動くようにしてみたものがありますので、良かったらどうぞ。

Quicklispのlocal-projectsに設置でもすれば、

(ql:quickload :spiceflavors)

で読み込めます。

FlavorsとCommon Lispについて

 現在のANSI Common Lispには、CLOSが含まれていることと、CLtL1の時代にはオブジェクト指向システムが含まれていなかったことから、CLOS以前にはオブジェクト指向システムはCommon Lispに搭載されていなかったと思う人が多いですが、実際のところは、1984〜6年当時のメジャーな処理系はCommon Lisp+Flavorsで処理系を配布/販売していて、Flavorsはほぼデファクトな存在でした。
主なところでは、

等です。ほぼCLtL1部分だけのKCLと、オブジェクト指向システムにはObject LISPを添付していたMCLにはFlavorsは付いてこなかった様子。ちなみにObject LISPの元祖はLMIなのでLMIにも含まれています。

試してみる

 今回もオブジェクト指向システムの紹介の度に書いているBankAccountというのを書いてみます。

 FlavorsとCLOSの主な違いといえば、どちらも多重継承ですが、Flavorsはマルチメソッドではありません。
また、Smalltalkのようなメッセージ送信が基本です。
さらに、Flavorsでは、インスタンス変数はメソッドのボディのスコープ中から参照できます。
今回のBankAccountでは、dollarsはメソッドがオーバーライドされるのでインスタンス変数としてアクセスしていませんが、price-per-share/num-sharesはインスタンス変数としてアクセスしてみています。

(defpackage :spiceflavors.demo
  (:use :cl :spiceflavors)
  (:shadowing-import-from :spiceflavors
                          . #.(intersection (*:list-external-symbols :spiceflavors)
                                            (*:list-external-symbols :cl)
                                            :test #'string=)))

(cl:in-package :spiceflavors.demo)

(defflavor bank-account ((dollars 0)) () :initable-instance-variables :gettable-instance-variables :settable-instance-variables)

(defmethod (bank-account :deposit) (x) (send self :set-dollars (+ (send self :dollars) x)))

(defmethod (bank-account :withdraw) (x) (send self :set-dollars (max 0 (- (send self :dollars) x))))

(defparameter *my-account* (make-instance 'bank-account :dollars 200))

(send *my-account* :dollars) ;=> 200 (send *my-account* :deposit 50) ;=> 250 (send *my-account* :withdraw 100) ;=> 150 (send *my-account* :withdraw 200) ;=> 0

(defflavor stock-account ((num-shares 0) (price-per-share 30)) (bank-account) :initable-instance-variables :gettable-instance-variables :settable-instance-variables)

(defmethod (stock-account :set-dollars) (x) (setq num-shares (/ x price-per-share)) (send self :dollars))

(defmethod (stock-account :dollars) () (* num-shares price-per-share))

(defparameter *my-stock* (make-instance 'stock-account :num-shares 10))

(send *my-stock* :dollars) ;=> 300

(send *my-stock* :set-dollars 600) ;=> 600

(send *my-stock* :deposit 60) ;=> 660

(send *my-stock* :num-shares) ;=> 22

(send *my-stock* :withdraw 120) ;=> 540

(send *my-stock* :num-shares) ;=> 18

この例を書いていて判明しましたが、インスタンス変数の初期値を継承してこないバグがあるようです…。
移植によるものなのか、そもそもこういう仕様なのかは調査してみたいと思いますが、Allegro CLに付属してくるFlavorsと、VAX LISPに付属のFlavorsでは初期値は継承してくるようです。
ちなみに、上記では、stock-accountのdollarsは、bank-accountのdollars変数にアクセスしないため問題なく動きます。

まとめ

 今回は、Spice Lisp: Flavorsを紹介してみました。
今となっては伝説のFlavorsですが、実装は結構あるようです。
手軽に試すとすれば、Allegro CLで(require :flavors)するのが一番かもしれません〈最新の9.0の試用版でも可能〉。

comments powered by Disqus