#:g1: frontpage

 

ANSI Common Lispの規格書(に近いもの)のPDFを入手する

Posted 2016-06-30 17:58:13 GMT

ANSI Common Lispの規格書を入手となれば当然ANSIが出しているので、ANSIから入手することになります。
現在PDFで$60のようなので約6,000円でしょうか。

しかし、このPDFは、紙媒体をスキャンした画像をPDFとしていて、検索もできないし電子媒体としては出来がひどいことで知られています。

さて、では、お馴染のLispWorksのHyperSpecや、FranzのFranz online ANS for Common Lispはどこから電子データを持ってきているのかというと、ANSIに持ち込む前のドラフトを元にしています。

HyperSpecの方はどの時点のものかは不明ですが、Franzの方は、dpANS2を元にしているという解説ページがあります。

このページにはdpANSが3つあると解説されていますが、この3つのdpANSは、CMUのAIリポジトリ等入手可能です。

今回は、このうちdpANS3RからPDFを作成してみます。

dpANS3RからPDFを作成する

用意するもの

  • dpans3のdviファイル
  • DVI→PDF変換ツール(dvipdf)
  • PDF結合ツール(pdftk)

まず、上記CMUのサイトからdpans3r.tgzとdpans3.tgzをダウンロードします。

$ wget http://www.cs.cmu.edu/afs/cs/Web/Groups/AI/lang/lisp/doc/standard/ansi/dpans/dpans3.tgz
$ wget http://www.cs.cmu.edu/afs/cs/Web/Groups/AI/lang/lisp/doc/standard/ansi/dpans/dpans3r.tgz

あとは展開して、DVIファイルをPDFに変換し、結合するだけです。

$ tar xvf dpans3.tgz
$ tar xvf dpans3r.tgz
$ mv dpANS3R/* dpANS3/
$ cd dpANS3
$ gunzip *.dvi.Z
$ for f in chap-*.dvi;do dvipdf $f;done
$ pdftk chap-0.pdf chap-1.pdf chap-2.pdf chap-3.pdf chap-4.pdf \
chap-5.pdf chap-6.pdf chap-7.pdf chap-8.pdf chap-9.pdf chap-10.pdf \
chap-11.pdf chap-12.pdf chap-13.pdf chap-14.pdf chap-15.pdf \
chap-16.pdf chap-17.pdf chap-18.pdf chap-19.pdf chap-20.pdf \
chap-21.pdf chap-22.pdf chap-23.pdf chap-24.pdf chap-25.pdf \
chap-26.pdf chap-a.pdf cat output dpANS3-15.17.pdf

以上の操作で、電子データとして中身の検索も可能な、dpANS3-15.17.pdfが生成されます。

なお、手元のANSI INCITS 226-1994(R2004)のPDFのページ数を確認すると表紙も含めて全体で1153ページですが、dpANS3では1360ページあります。
上述のCL Untold Storyによると、縦方向のスペースを調整した結果200ページも圧縮されたらしいです。

まとめ

ANSI規格の著作権は、ANSIが所有することになるようです。
規格をまとめたコミュニティ側としては、パブリックドメインにしたかったようですが、この辺りの経緯はCommon Lisp: The Untold Story - nhplace.comで語られています。

dpANSがリファレンスとして加工されて利用されるのにはこんな経緯がありますが、dpANSのTeXについては、Kent Pitman氏の見解によれば、派生、再配布、自由とのことです。


HTML generated by 3bmd in LispWorks 7.0.0

LispWorks IDE起動時のツールを指定する

Posted 2016-06-25 13:55:41 GMT

問題:

起動時のデフォルトだとGCモニターとリスナーが起動してくるがこれを変更したい

解決策:

lispworks-tools::*default-tools* で指定する

デフォルトだと、lispworks-tools::*default-tools*は、(lispworks-tools:lispworks-echo-podium lispworks-tools:listener)ですが、これを変更することで起動時のツールを変更できます。

設定は、~/.lispworks 等で可能です。

;;; ~/.lispworks
(setq lispworks-tools::*default-tools* '(lispworks-tools:editor))

仕組み

デフォルトのツールは、lispworks-tools:start-lispworksで起動しますが、起動時のツールは、:tools引数で指定することになっていて、初期値は、lispworks-tools::*default-tools* になっています。

lispworks-tools::*default-tools*の初期値は、(lispworks-tools:lispworks-echo-podium lispworks-tools:listener)です

所感

マニュアルに記載がないようですが、これは書いてて欲しい……。

  • env:start-environment
  • method-function env-internals:environment-start env::capi-environment

disassembleして眺めて、lispworks-tools:start-lispworksに辿り着き、lispworks-tools::*default-tools*変数を見付けました。


HTML generated by 3bmd in LispWorks 7.0.0

GNU Emacs の ledit-mode の謎

Posted 2016-06-22 15:15:10 GMT

EmacsにはLisp編集支援モードのledit-modeというものがかなり古くから標準で同梱されています。
しかし、このledit-modeですが実際に使っているという人や、利用方法の紹介などのブログ記事もこの十数年で見掛けたことは一度もありません。
ledit-modeのソースを眺めてみると分かりますが、現在では使い方も良く分からない代物となっています。

leditといえば、MACLISPをEmacsから利用する環境の名前でもあるのですが、ソース上のキーバインドから類推するに、どうもそれをGNU Emacs + Franz Lispで実現したもののように思えます。

以前、Franz LispをSIMH上のUltrix 4.0上で動かしてみたりしましたが、最近rshでのファイル転送も上手く行くようになったので、VAX Ultrix用にビルドされたEmacsとFranz Lispでこのledit-modeがどのように機能するのか試してみることにしました。

VAX UltrixのEmacsを用意する

世界にはVAX UltrixでGNU Emacs 21.4.1をビルドして配布している酔狂な方がいらっしゃるので、ありがたく使わせて頂きます。

/freeware以下に展開するのが前提のようなので、/freewareディレクトリを作成して展開します。

leditの設定

Emacs側だけでなく、Franz Lisp側でもledit.lleditcfns.o というファイルが必要になります。
(以前はGNU Emacsに添付されてきていたようですが、現在は添付されていないようです。)
leditcfns.cがあるディレクトリで、make leditcfnsすれば、leditcfns.oができますので任意のディレクトリに配置します。
Franz Lispの標準のライブラリのディレクトリは、/usr/lib/lispのようなので、下記では、ここに設置することにします。

次に、設置場所に応じてledit.lleditcfns.oの読み込み場所を書き換えます(標準のパスであればファイル名のみで良いようです)

(cfasl "leditcfns.o" '_switch_to_proc 'emacs)

後は

$ liszt ledit

で、ledit.oを生成することができるので、これをまたライブラリパスに置いておきます。

ledit の操作

MACLISPのleditと同じく、Emacsとlisp処理系を行ったり来たりするのにUNIXのジョブコントロールを利用するようです。
Emacsをサスペンドさせ、処理系を起動し、処理系をサスペンドし、Emacsを起動し……というのを交互に繰り返します。
Emacsからは、Franz Lispのインタプリタのlispと、コンパイラのlisztに移行することが可能です。

利用に際して、lisplistz、をバックグラウンドとして起動してemacsを起動します。

なお、Emacs側のコマンドは下記の通りです。

  • ledit-save-defun (c-m-d)
  • ledit-save-region (c-m-r)
  • ledit-go-to-lisp (c-x z)
  • ledit-go-to-liszt (c-m-c)

試しに下記のようなファイルの場合、

(load 'flavors)
(load 'describe)

(defflavor foo (x y z) () :initable-instance-variables :settable-instance-variables :gettable-instance-variables)

(setq foo (make-instance 'foo ':x 0 ':y 1 ':z 2))

(describe foo)

Emacsのバッファ上で、c-x h c-m-r c-x z すると、lisp に切り替わって、

%?lisp

[3]+ Stopped emacs bash$ %?lisp lisp ;Reading from LEDIT: [load /usr/lib/lisp/flavors.l] [fasl /usr/lib/lisp/vanilla.o] ; t ; foo ; fclosure[6] fclosure[6], an object of flavor foo, has instance variable values: x: 0 y: 1 z: 2

; fclosure[6] t ->

というような結果になります。
ここからemacsに復帰するには、c-eの後に改行を入力します。

まとめ

Emacsのledit-modeを実際にFranz Lispとの組み合わせで動かしてみました。
最新のEmacs 25でもledit-modeは(require 'ledit)で利用することが可能なようですが、

  • ターミナルで動かすことが前提
  • 最新のGNU EmacsとFranz Lispが同時に動く環境は存在しない
  • Franz Lisp側のコードが添付されてこない

等の理由で、実質機能しないライブラリとなっています。
削除してしまっても困る人はいないと思われますが、恐らくledit-modeが何物なのか不明なので削除して良いのかも判断できず、そのまま生き残っているのではないでしょうか。
一体いつまでGNU Emacsに添付され続けるのかこれからも見守り続けたいと思いますが、Franz Lispを最新の環境で動くようにすれば、このライブラリは生き返ることになりますし、Franz Lispを最新の環境でビルドできるようにする酔狂な人の登場も期待しています。


HTML generated by 3bmd in LispWorks 7.0.0

Common Lispのクラス構造のMembership Loopsの謎

Posted 2016-06-12 14:30:43 GMT

Common Lispのクラス構造では、

(type-of (find-class 'standard-object))
;=> standard-class 

(type-of (find-class 'standard-class))
;=> standard-class 

(subtypep 'standard-class 'standard-object)
;=> t 
;   t 

という風に循環を有する構造になっています。

しかし、この仕組みがどのような理由で採用されたのかについては、めぼしい資料もないようです。
Lisp仲間の小出さんとお話をすると、RDFでも同様の構造が現われているそうで、Common Lispがこの構造を採用した理由が知りたいものだ、という話に度々なっていました。

この循環をRDFでは、Membership Loopsと表現しているようなので、このエントリーでもMembership Loopsと呼ぶことにします。

小出さんはセマンティック・ウェブを専門とされていますが、このMembership Loopsについても以前お書きになっています。

Common-base vs. Mixin-base

以前、1988年辺りにまとめられていたCLOSの仕様のMOPの部分をTeX原稿からorgモードのファイルに変換して眺めたりしてみていましたが、

この資料を改めて眺めてみると、「Class Organization in the CLOS Kernel」の段で、


Class Organization in the CLOS Kernel

The earlier version of this section has been removed since it was too concerned with design rationale and not concerned enough with the user ramifications of the design decision.

This section should layout the class organization we decide on and then describe how it is supposed to be used. It will need to talk about the how users are expected to define subclasses of standard-class which specialize or override behavior.

For now, the class organization described below is the one (in PCL) where all the classes are subclasses of standard-class and standard-class is a subclass of object. Once we get that locked in, we will have to write this section.


とありました。
どうも以前のバージョンには詳しく書いてあったようですが、時期尚早ということで削除になったようです。
それならばということで、以前の版を探してみたところ、1987年の9月頃の版には詳しい記述がありました。

長いですが、そのまま引用すると、


Class Organization in the CLOS Kernel

The purpose of this section is to present the design rationale for why we defined standard-class as one class rather than as a subclass of the minimal class and some mixin classes for other features. The reason this belongs here is because you need to be able to understand the philosophy behind the design of the current class structure to understand how to program with it. In the following section, we present the standard classes defined in the CLOS kernel.

In the design of the lattice of these classes, there is a major design choice that deserves some discussion. The two choices that we want to contrast are the mixin-base versus common-base designs. (The style implemented in CLOS is the common-base design.) For this part of this discussion we will only talk about design of the standard metaclasses, and then extend it to the other metaobjects.

In the mixin-base design style, features of classes would be be partitioned among a number of different mixins, each of which supports a particular capability. Standard-class would then be a direct subclass of an appropriate set of these mixins, built-in-class would have another set of superclasses, etc. For example, there would be a class, called say primitive-class, that contained just those slots and methods that any object that could be a class must have. Other mixins, including something like obsoletable-metaclass, instance-slot-metaclass, class-slot-metaclass, (or should it be slotted-class) initarg-metaclass would also be direct superclasses of standard-class. These would each bring in to standard-class a few slots and behaviors that were appropriate to that type of class. Other metaclasses, such as built-in-class and structure-class would have the appropriate subset of these and other mixins.

In the common-base design style, the class standard-class is the root of the metaclass lattice, and all other metaclasses are subclasses of this class. In this design, all the features in standard-classes are implemented directly for standard-class. For subclasses of standard-class like built-in-class, features that are not allowed must be “turned off” by specializing the appropriate generic function.

It would be nice to have a picture here. We just put defclass forms for now because drawing pictures is so hard.

Common base:

(defclass standard-class ()
    (..))
(defclass built-in-class (standard-class)
    (..))
(defclass structure-class (standard-class)
    (..))
(defclass forward-referenced-class (standard-class)
    (..))
(defclass funcallable-standard-class (standard-class)
    (..))

Mixin base:

(defclass primitive-class ()
    (..))
(defclass nameable-class (primitive-class)
    (..))
(defclass built-in-class (nameable-class)
    (..))
(defclass structure-class (slotted-class nameable-class)
    (..))
(defclass slotted-class (primitive-class)
    (..))
(defclass standard-class (obsoletable-class slotted-class)
    (..))
(defclass funcallable-standard-class (standard-class)
    (..))

Arguments for mixin-base style:

By dividing the functionality of standard-class into many mixins, one can potentially define mixins that capture behavior associated with a form of type abstraction. This might allow easier understanding of the capabilities of any class that includes a subset of these type mixins, since the behaviors of each mixin should be independent and additive. Use of these “type” mixins to add behavior to a class implies that there is no need to turn off unwanted behavior, since all behaviors are invoked through specific generic functions. Unwanted behaviors would have no applicable method for classes that did not include such a mixin. Test for the availability of a particular behavior can be done using typep, since behaviors are intimately tied to classes.

Arguments for common-base style:

All the classes in CLOS are implemented as instances of standard-class. By including in one class all the behavior useful for standard-class, one gets a minimal self supporting kernel. This is minimal in several senses. First, the bootstrap implementation of classes need only have the single class standard-class that is its own class. Secondly, once one has understood the features of standard-class one has a complete view of the underlying implementation. Finally, this requires fewer classes in the kernel.

Using the common-base style, one does not have to make and defend decisions about the division of behaviors among mixins. For example, should there be one mixin or two that implement the notions of class and instance slots. Should there be a separate mixin for the standard initialization mechanism. Should there be a separate mixin that support default-initforms. Having the mixins is fine if you have cut the world exactly right for the variation of class behavior one wants. However, if not, then one has the same type of problem with overriding unwanted behavior with multiple mixins.

The test for availability of a particular behavior can be done by defining a generic-function that acts as a predicate, making the default method return NIL, and defining a method for any class that supports the behavior subclasses which “turn off” that behavior must redefine this method. Although this requires some extra mechanism, it allows independent implementation of a behavior in two different classes without insisting that to support the same behavior one needs to share a common superclass.

Another reason why we want to encourage users to make their classes subclasses of either standard-class or structure-class is that we don't specify a portable mechanism for extending the type system. In other words, the user must use the mechanism provided by standard-class or structure class for determining the class of objects. Finally, there is an argument that the current Common Lisp type system is not partitioned into mixins that support common behavior (e.g. where is ceiling for float and rational). Hence trying to use this style in the rest of CLOS wouldn't make the system be uniform in any way.

For us the balance of these arguments weighed in favor of the common-base style. Thus for each of the classes in the kernel below, there is a standard-x class at the top of the lattice that captures the common behavior. For unwanted behaviors, there are methods on generic-functions that signal an appropriate error. For example, for built-in-class, an error is signalled if the user tries to update the class an provides slots for the class. In the description of the generic function in the kernel, each method that has an error case is indicated.


という風に、common-baseとmixin-baseが検討されていたようです。
それで紆余曲折があってcommon-baseが採用されたと思われますが、ここから決定までの経緯はあいかわらず不明です。

この時期のCLのオブジェクト指向システムのメーリングリストの議論も眺めてみると、何点か該当するものがあります。

ざっと眺めたところでは、mixin-baseの方が筋は良いだろうとされているようですが、

  • 上手く機能やプロトコルをmixinとして分割できるとは限らない。
  • 既に参照実装であるPCL(Portable CommonLoops)では、common-baseでやってきて、それなりの知見もある

等々の理由で、その後mixin-baseが推されることもなく、そのままcommon-baseの流れになっていったようにも見えます。
正しい方法が分からないので実装が選択できるようにすべき、という話もあったようですが、その後特に誰かが行動を起こしているような所もなく、何よりmixin-baseが実装され検証される、ということもなかったようです。

しかし、現状の構成では、standard-classに一本化されている訳でもなく、built-in-class や、 structure-class 等も存在するので、明示的なmixin元は存在しないものの、standard-class とは違う属性をmixinしている何かとも解釈できなくもなさそうです。

まとめ

どうやらCommon LispのMembership Loopsについては、RDFのように理論的裏打ちがあった上での構成ではなく、機能の分割も検討してみたけど、丸ごとで提供する今の構成に落ち着いてしまった結果に見え、クラスや集合論的見地からの構成ではなさそうに見えました。

ちなみに、Common Lisp Object System Specificationの3章は完成することはなかったのですが、今回saildartのTeX原稿をPDFにしてみたら、それらしき体裁のものができたので興味のある方は見てみて下さい。

また、この件について何か詳細をご存知の方は是非とも教えて頂けると嬉しいです。


HTML generated by 3bmd in LispWorks 7.0.0

Common Lisp Recipes を読んだ

Posted 2016-05-26 16:22:50 GMT

前にこのブログでも、

''755ページもあり、まだ自分は7章までしか読んでいないですが、Common Lispを日常的に書く人には必携の書という感じです。
全部読んだら感想でも書いてみようかなと思っています。''

などと書いていましたが、Common Lisp Recipes — A Problem-Solution Approach(以下CLR)を半年掛けてやっと読み終えました。
当初は、一日75ページのペースで読み進めていたので1月中に読了かと思っていましたが、何ヶ月か放置してしまっていました。
しかし、引っ越しをして通勤時間が激増したのでKoboに入れて通勤時間中に読むようにしたところ約2週間で読了となりました。

この本の対象読者について

この本の前書きにも書いてありますが、基本的な所は、Practical Common Lisp(実践Common Lisp)を参照することとして、その次のステップに当たる実践的なレシピ集という位置付けになっています。 マクロ等の解説も、On LispやLet Over Lambdaがあるので、そちらに任せているということで棲み分けがされているようです。

CLRは、On LispやLet Over Lambdaのように、なんだか良く分からないが凄そう、というような所は全くなく外連味もないので、ガチでCommon Lispを書いてないとあまり嬉しくない内容なのが特徴でしょうか。
しかし、ガチで書いている人には必読の内容で、実に細かく実践的に解説されているので、実践Common Lispを読了した位の人であれば、通読すればCommon Lispの書法が1ランクアップするに違いないと思います。
また、ある程度他の言語を齧ってからCommon Lispに入門する人が多いと思いますが、他の言語の経験から類推していても分からないような部分についての解説が大盛りです。Common Lispらしい方法と用意された道具を知るにもとても良い本だと思います。

また、Stack Overflowで質問されそうな基礎的な大体載っているので頭に入れておくと学習時間の節約にもなりそうです。

いまどきのライブラリの解説が少ないという指摘もあるようですが、Common Lispに備わっている機能をかなり詳しく解説していて、個人的にはCommon Lisp固有の機能を徹底的に解説しているこのような本のほうが貴重だと思いました。

CLRがCommon Lispの基本機能の解説を押えてくれた、とも考えられるので、今後さらに応用寄りの解説書が出版されることを期待したいところです。

そういば、MOP周りはこの本ではあまり解説されていませんので、その辺りも今後の方々に期待したいところです。

一応Common Lispのエキスパートも想定読者層ではないようですが、この本で取り上げられている事項すべてに通暁している人というのは多くはなさそうです。

印象に残ったところ

まず、シンボルの解説から始めているのが渋いです。
また、配列、ストリーム、コンディション周り解説はなるほどなと思うことが多かったです。

気になるところ

全編サンプルコードの書法では、関数をfunction(#')ではなく、quote(')で記述していますが、これがちょっと個人的には残念です。
また、functionを100%排除しているわけでもなく、ちょっと不思議でもあります。
書籍としての見栄えを優先したのか個人の趣味なのか。
quoteにしてしまうと、情報量が落ちるのであまり良いスタイルとも思えないですし、古えの1970年代Lispっぽいです。

例えば、 (map 'list 'list '(a b c))

と書くと型としての'listと関数としての'listが並ぶので気持ち悪かったり(この書籍でもmapの関数では、#'が使われていたりします)

まとめ

Common Lispをガチで書く人で、より知識を深めたい人は、買って損はないです。
Apressでは電子書籍はセールで価格が40%offになったりすることがままあるようなので、それを狙って購入してみるのも良いかもしれません。


HTML generated by 3bmd in LispWorks 7.0.0

Chez Scheme がオープンソース化されたので試してみる

Posted 2016-05-05 14:59:16 GMT

先日の4/26日の話になりますが、商用Scheme処理系の雄であるChez SchemeがApache License, Version 2.0で公開されました。

結構反響は大きいようですが、早速ダウンロードして試してみます。

$ git clone https://github.com/cisco/ChezScheme.git
$ cd ChezScheme/
$ ./configure --threads --64

今回試すホストはLinuxですが、何も指定しないとシングルスレッド版になるようです。
上記では、マルチスレッド版になるように指定しています。これでビルドすると、ta6le版がビルドされます。

また、

$ make test

でビルドのチェックも可能です。
しかし、トレースの出力結果もテストするのは初めてみたような…。
なお、make checkには結構時間がかかります(多分1時間以上)
ta6le/mats/summaryに結果が出力されます。

インストールは、

$ sudo make install

のようになります。 Chezの処理系であるscheme、お馴染のPetite Chezのpetite、それとスクリプト実行用なのか、scheme-script等々がインストールされます。

起動してみる

$ scheme

で起動します。

$ scheme
Chez Scheme Version 9.4
Copyright 1984-2016 Cisco Systems, Inc.

>

なにはともあれfibを定義してみましょう

(define (fib n)
  (if (< n 2)
      n
      (+ (fib (- n 1))
         (fib (- n 2)))))

(time (fib 40)) ;>> (time (fib 40)) ;>> no collections ;>> 0.958591374s elapsed cpu time ;>> 0.958607953s elapsed real time ;>> 0 bytes allocated ;=> 102334155

さすがネイティブコンパイラだけあって速いですね。
LispWorks(ネイティブコンパイラ)位の速度が出てます。

他に何か試してみようと物色していましたが、 @nfunatoさんにChezでも採用されているNanopass Compilerの動画を教えてもらったので、これを眺めつつ、この動画で紹介されている scheme-to-c を試してみます。

Clojure Conj 2013: Andy Keep - Writing a Nanopass Compiler

scheme-to-c は nanopass-framework に依存していますが、下記の版の nanopass-framework-scheme でないと上手く動かないようです。

それぞれgit cloneすればOKです。

使用方法はREADMEに書いてありますが、下記ではコマンドラインからのオプションの指定ではなく、処理系内からライブラリのディレクトリの指定と読み込みを実行しています。

(cd ".../scheme-to-c")

(library-directories (cons '(".../nanopass-framework-scheme" . ".../nanopass-framework-scheme") '(("." . "."))))

(import (c))

またまたfibを実行

(time
 (my-tiny-compile
  '(letrec ((fib (lambda (n)
                   (if (< n 2)
                       n
                       (+ (fib (- n 1))
                          (fib (- n 2)))))))
     (fib 40))))
(time (my-tiny-compile (quote (...))))
    no collections
    0.003715930s elapsed cpu time
    1.254270947s elapsed real time
    155104 bytes allocated
102334155

scheme-to-cはCに変換して実行し、結果をChezに戻してくるというものですが、コンパイルの速度も実行結果も速いですね!

(trace-passes #t)

を実行することでトレースも可能で、ナノパスコンパイルの細かいパスの具合や、変換されたCのコードなども眺めることが可能です。

まとめ

速さを求めるならRacket、Larceny、Chickenあたりかと思っていましたが、Chezはさすがネイティブという速度です。
これだけ速いと比べてみたくなりますが、Chezが公開されたこのタイミングでベンチを取って公開する人も現われました。

まだ一覧としては未完成のようですが、これまでの所でもやはりChezは高速なようです。


HTML generated by 3bmd in LispWorks 7.0.0

Common Lispの機能大盛りでズンドコキヨシ

Posted 2016-03-21 17:12:12 GMT

Common Lispの機能大盛りでズンドコキヨシを書いてみました。
大分無理矢理です。

(eval-when (:compile-toplevel :load-toplevel :execute)
  #+cmucl   (import 'pcl:funcallable-standard-class)
  #+sbcl (import 'sb-mop:funcallable-standard-class)
  #+allegro (import 'mop:funcallable-standard-class)
  )

;;; utilities (defun lsh&inc (n delta bytespec) (ldb bytespec (+ (ash n 1) delta)))

(define-modify-macro lsh&incf (delta bytespec) lsh&inc)

;;; (defconstant kiyoshi-state #b11110 "ズン = 1, ドコ = 0 ズンズン ズンズン ドコ = 11 11 0")

(define-condition kiyoshi () ())

(defclass zundoko-function (standard-generic-function) ((state :initform 0)) (:metaclass funcallable-standard-class))

(defgeneric zundoko (zd) (:method ((zd (eql 0))) (write-line "ドコ")) (:method ((zd (eql 1))) (write-line "ズン")) (:generic-function-class zundoko-function))

(defmethod update-zundoko-function-state ((zd integer) (gf (eql #'zundoko))) (lsh&incf (slot-value gf 'state) zd (load-time-value (byte (integer-length kiyoshi-state) 0))))

(defmethod reset-zundoko-function-state ((gf (eql #'zundoko))) (setf (slot-value gf 'state) 0))

(defmethod zundoko :after (zd) (when (= kiyoshi-state (update-zundoko-function-state zd #'zundoko)) (write-line "キ・ヨ・シ!") (reset-zundoko-function-state #'zundoko) (signal 'kiyoshi)))

(handler-case (loop (zundoko (random 2))) (kiyoshi ())) ;>> ドコ ;>> ズン ;>> ドコ ;>> ズン ;>> ズン ;>> ドコ ;>> ズン ;>> ズン ;>> ズン ;>> ドコ ;>> ズン ;>> ズン ;>> ズン ;>> ズン ;>> ドコ ;>> キ・ヨ・シ! ;>> ;=> nil #-allegro (quit) #+allegro (exit)


HTML generated by 3bmd in LispWorks 7.0.0

Lisp都市伝説を検証する: KCL登場以前にCommon Lispの処理系は存在しなかった、は本当か

Posted 2016-02-01 14:35:08 GMT

KCL(Kyoto Common Lisp)は、1984年に登場したCommon Lisp処理系です。
名前にKyotoと冠するように日本発の処理系で、湯浅太一先生と萩谷昌己先生の御二方が中心となって作製されました。 この処理系に思い入れのあるユーザーは日本には多いようですが、無償で配布されたため日本の大学等で広く使われたようです。
KCLは後に、テキサス大学オースティン校のBill Schelter氏によってAKCL(Austin Kyoto Common Lisp)としてメンテナンスされ、そこからGNUに引き継がれ、現在GNU Common Lispとして知られています。
派生の処理系は沢山あるのですが、KCL系の処理系でANSI Common Lispに準拠したものとしては、ECLやMKCLがあります。

さて、そんなKCLなのですが、「KCLは世界初のCommon Lisp処理系なんだよ」などという話を良く耳にします。
その他色々尾鰭が付いたりしますが、

  • たった二人だけで
  • 仕様を読んだけで
  • 世界で始めて
  • 巨大な仕様のCommon Lisp処理系を作成した
  • 世界中の計算機科学者が仰天した

あたりのコンビネーションが大体のようです。

例を挙げると、

紫藤のWiki: GCL - GNU Common Lisp

1984年にGuy SteeleによってCommon Lisp the Languageが纏められるも、「こんなデカい仕様の言語、一体どこの誰が実装出来るんだ?」と誰もが頭に来ているところに、いきなり日本でKCLが登場したのに世界中の人々はビックリしたらしい。 従って、KCLは世界で殆ど初めて登場したCommon Lisp処理系である。

古都がはぐくむ現代数学: 京大数理解析研につどう人びと

第六章 応用の「花畑」から 第一景 Kyoto Common Lisp を作ったつわ者たち p223

『しかし、問題はあった。言語の仕様はできていても、実際のコンピュータ上で動く言語処理系としてのCommon Lispはまだ存在していなかった。設計図はあっても「建物」はまだ誰も建てていなかったのである』 正式な仕様も完成していなかった

「仕様書を読んだだけでこんな巨大なシステムを作った!?」。驚く計算機科学者は多かったという。Common Lispの処理系がまだ存在しなかった世界では、二人の「無謀な開発」に仰天したのだ。

のようなものがあります。Wikiのようなライトなものから書籍まで幅広いようですが、以下、伝説を眺めつつ検証してみたいと思います。

たった二人だけで

これはコアな部分は御二方で作製されていたようなので、そんな感じのようです。
1984年のコンピュータソフトウェア1(2)の「KCl: / 湯浅太一、萩谷昌己」という記事によれば、DG(データゼネラル)の矢部輝夫、原田年康、両氏に謝辞があります。
両氏の担当としては、マシンに依存な低レイヤー部分や、エディタ等々の作製で活躍されたようです。
開発チームの最大人数としては4人とも考えられます。

仕様を読んだけで

これは本当です。引用文献で出てきますが、

  • Common Lisp Reference Manual, Laser Ed., CMU 1982
  • Common Lisp Reference Manual, Mary Poppins Ed., CMU 1983

の参照のみで作製したようです。
Common Lisp Reference Manualは、正式は出版時には、お馴染のCommon Lisp the Lnaguageとなります。
Mary Poppins Ed. は完成の一歩手前のようです。
正確には、仕様の草稿を元に処理系が作られたということになり、Common Lisp the Languageの発表前に完成している謎はこの辺りに原因があります。

なお、Common Lisp策定グループとは独立して作られた処理系ということで、仕様に記述がない暗黙の規則等々を洗い出すことになりました。
「The Evolution of Lisp」でも、この点が非常に評価されたと回想されています。

世界で初めて/まだ誰も処理系を作っていなかった

これは単純に事実と違います。 上述の御二方のKClの記事でも、

米国ではPERQ上のSpice Lispをはじめ、いくつかのCommon Lispシステムがすでに稼動していると聞くが、我が国ではまだCommon Lispはよく知られていないようである。

と書かれていますが、1982年のLFPでのCommon Lispのお披露目時点で、作成中のCommon Lisp処理系として、

  • Lisp Machine(Symbolics、LMI、MIT)
  • Spice Lisp
  • VAX LISP
  • NIL
  • TOPS-20 Common Lisp
  • S-1 Lisp

が挙げられています。

(前例の無い)巨大な仕様のCommon Lisp処理系を作成した

上記の最初期の処理系のうちCommon Lispの仕様の下敷になったのは、Spice Lispのマニュアルでした。
また、NILは、1979年からMacLISPの後続として開発がスタートしましたが、途中からCommon Lispとなりました。
Common Lispへの重要な影響としては、NILがレキシカルスコープを採用していた為、Common Lispもそれを取り入れることになったようです。

他にLispマシン上のLisp Machine Lispがありますが、これら大別すると3つのMacLISP系方言の共通のサブセット的なものを作ろうというのが、そもそものCommon Lispのスタートです。
共通仕様策定の動機としては、ARPA(DARPA)からLisp系プロジェクトに資金援助をしても良いけれど、それぞれ小さく分散しつつ競合しているような状況で資金援助するのは無駄で嫌なので統一して欲しい、という注文がバックグラウンドにあったようです。

そういう感じのなので、当時の人達の認識としては、Common Lispは新しい機能もあるとはいえMacLISP系の共通のサブセットであり、全く新規の前人未踏の山という認識では全然なかったといえます。

実際LispマシンのLisp Machine Lispの方がCommon Lispより何倍も大きく、NILもCommon Lispよりは大きいです。

こんな感じだからなのか「世界初のCommon Lisp処理系」という点については、そういう判断基準や価値観や興味がなかったらしく、当時の資料を沢山眺めてみても、そのような記述が全然ありません。

世界中の計算機科学者が仰天した

これは当時の熱狂を知るすべがないのですが、現在残る資料を眺めても、これが元ネタか、というようなものはありません。

1984年の6月にCommon Lispの策定グループにKCLのグループがKCLについてメールをしたようなのですが、ここで初めてCommon Lispの策定グループに認知されることになったようです。
最初の報告を受けてFahlman氏は、Spice Lispキットのコードを元に移植したのだろう、と勘違いしていたようですが、策定グループとは全く別個にスクラッチで書かれたと知り賞賛していたようです。

There has been some confusion, partly my fault, about whether the Kyoto
University version of Common Lisp is based on our portable Spice Lisp
code.  I have just received a letter from Taiichi Yuasa of Kyoto
University which makes clear that their implemenation is TOTALLY
INDEPENDENT of our code.  They did receive some early drafts of the
manual, papers, and benchmark code from us (by way of Nippon Data
General), but they did not receive any Lisp system code from us until
several months AFTER their implemenation was finished and running.  I
just wanted to set the record straight.  They apparently got this
implementation running from a standing start in five months with a team
of four people, with no direct input at all from the Common Lisp
designers -- an awesome performance.

To further clarify things, Data General has their own implemenation for the Eclipse. This is the one that was demonstrated at AAAI, and it IS based on our code, though the task of porting it to Data General hardware was done with essentially no help from CMU.

-- Scott

当時、Common Lispの普及の一環として、Spice Projectでは、Spice Lispのコードのマシン依存の部分を書き換えたり、付け足したりすれば動くようなCommon Lispのキットを配布していたようです。
このキットを利用した処理系としては、上記のVAX LISP、TOPS-20 Common Lisp等々があります。
また、bit Vol.17 No.6 1985: Common Lisp 入門(3)によれば、SymbolicsもCommon Lispのエミュレータを配布していたようで、新しく提案される仕様を検証する上で両者は中核になっていたようです。

こういう背景があるので、これらのキットを利用していないこと、また、Common Lispの策定グループとのやりとりもなく仕様書のみで短期間で実装されたことが賞賛された、という所のようです。

とりあえず、実現できるかも分からない巨大な仕様を初めて動くものとして提示して世界を驚かせた、という感じではないことが分かると思います。

まとめ

KCLの業績は偉大です。
偉大ではありますが、世のKCL伝説は想像が多々混じっているようで、ちょっと誇張しすぎな所もあるかなと思います。
実際に、湯淺/萩谷先生の報告では、そういう誇張は一切ないので後の人々が伝説化してしまったのだろうなと想像しています。

ちなみに、当時のKCLの記録は面白いの読んでみては如何でしょうか。


HTML generated by 3bmd in LispWorks 7.0.0

Lisp都市伝説を検証する: Common Lisp vs Scheme (1)

Posted 2016-01-20 13:25:57 GMT

Common LispとSchemeを対比して曰く

LISP系言語はSchemeとCommon Lispを二大潮流とするが、提案された機能を原則全て導入するCommon Lispに対して、成員の全員一致を原則とするSchemeという特徴を持っている。

これは、WikipediaのSchemeからの引用ですが、割と耳にすることが多いSchemeとCommon Lisp特徴の対比です。
しかし、Common Lispの歴史を眺めてみる限りでは、そんなことないよなあと常々思っているので検証してみました。

話の大元

日本語と英語のWikipediaのSchemeの項目を比較してみると、この記述は日本のものにしか存在しないようです。
以前、昔のbitを眺めていて似たような話を目にしたことがあったので探してみた所、大元になったと思われる話を発見することができました。

bit 1996-4、5月号に掲載された、Guy L. Steele Jr.の『Scheme 過去◇現在◇未来』(訳 井田昌之)というものに、ほぼそのまま該当する発言があるようです。

Schemeコミュニティはフレンドリーで…(中略)…Revised reportの作業をしていたときの委員会の大原則 は、「もし変更が提案された場合、誰かがnoと言ったらそれは入れられない」 ということでした。

これとCommon Lispコミュニティと比べてもいいかもしれません。Common Lisp はフレンドリーではないとも言えるでしょう。コンセンサスで動くのではなかっ た。…(中略)…「変更が提案されるとき、誰かがyesと言うとその機能は言語に入れられていきます!」だから、 Common Lispは大きくなりました。

検証

Scheme方面

GLSの話の1996年当時、Schemeは、R4RSでIEEE Schemeも決定、R5RSは作成途中だったようです。 ここまでは全会一致の原則は守られていた、というような話ですが、IEEE Schemeは、95%の賛成で決まったようなので既に崩れてしまっているような…。

Scheme Steering Committee Position Statement(2009)によると、IEEE Schemeが、95%の賛成、R6RSが、66%の賛成で議決されているということです。

R5RSの記述がないですが、IEEE Schemeの上位互換なので、全会一致の下敷にはなり得なそうです(反対だった人もやっぱ賛成という話はあるかもしれませんが…)、とはいえ、R6RS程の混乱もなかったようなので、1998年のR5RSまでは、古き良きScheme精神は保たれていた、という感じなのでしょう。

厳密にいうと、R4RS(1991)まででしょうか。

ちなみに、R7RSは全会一致で議決されたようですが、Scheme Steering Committee Position Statement(2009)があるように、90%の賛成で議決されるとなっているので、全会一致で〜というのはポリシー的にも過去の話になっているようです。

Common Lisp方面

「変更が提案されるとき、誰かがyesと言うとその機能は言語に入れられていきます!」とのことですが、Schemeと対比した誇張と思われ、発言の文字通りであったと考えるのは間違いだと思われます。

仕様策定時でも現在でも有用だと思われる機能で却下(放置)になった機能は沢山ありますが、代表的なものをピックアップしてみました。

ちなみに、ラムダキーワードをキーワードシンボルと同一化するのは、ISLispでは仕様に取り込まれましたし、大域/動的変数についてもISLispで割合に整理されました。
KMP氏は自身の提案を記録に残していることが多いですが中々の執念を感じます。

その他、

  • ローカルな定数宣言/フォーム → 議題にのぼるも立ち消え?
  • 繰り返し機構としてのSeriesの導入 → GLS他が推していてCLtL2に掲載したりするも取り入れられず(ちなみにSeriesの前身のLetSの提案もCLtL1成立前からあったので2回見送られているとも言える)

等々、議題になった提案から、メーリングリストでの提案、真面目なものから戯言まで沢山あります。

蛇足ですが、色々な提案が飛び交うCommon Lispの取り纏めに四苦八苦するGLSというイメージがありますが、GLSからの思考実験/ジョーク/釣り的な提案、ちゃちゃ入れは寧ろ多かったようです。 そういう時は大抵Quux名義なのですが、GLSの幅の広さを感じます。

まとめ

まとめてみると

  • Common Lispでは提案された機能を原則全て導入するなどということはなく、妥当なものでも見送られる(忘れられる)ことがあった
  • Schemeの仕様が全会一致で決まっていた時代は過ぎ去った

という所ですが、Schemeの性質の説明の為に誇張した話が言語ポリシー伝説にまで昇華してしまったのかなと思います。


HTML generated by 3bmd in LispWorks 7.0.0

スペシャル宣言を取り消したい

Posted 2016-01-13 16:07:59 GMT

年始の1/2にCommon Lisp Recipes — A Problem-Solution Approachを購入しました!
お値段は電子版で、$48.99 でした。
755ページもあり、まだ自分は7章までしか読んでいないですが、Common Lispを日常的に書く人には必携の書という感じです。
全部読んだら感想でも書いてみようかなと思っています。

ということで(もないですが) Common Lisp Recipes風にレシピの一つでも書いてみようかなと思い立ち、『スペシャル宣言を取り消したい』というネタで書いてみました。

問題: スペシャル宣言を取り消したい

(defvar var 42)

...

(defun foo () (let ((var 0)) (lambda () (incf var))))

(set 'bar (foo))

(loop :repeat 5 :collect (funcall bar)) ;=> (43 44 45 46 47) ??? (1 2 3 4 5)を期待していたが

クロージャーを作ってカウンターを作ったつもりだったが、意図しない挙動になった。
調べてみると、閉じ込めた変数と大域スペシャル変数との名前が被ったことが原因と分かったので大域のスペシャル宣言を取り消したい。

解決策: なし

一度シンボルに対して宣言してしまったら、スペシャル宣言を取り消す方法は規格にありません。
名前が被らないように、スペシャル変数には、*耳当て*を付けましょう。

しかし、ここでのポイントは、宣言はシンボルに対して、ということです。
ということで、処理系を再起動しなくても、該当のシンボルを破棄してしまえば、復帰できます。

(unintern 'var)

(compile (defun foo () (let ((var 0)) (lambda () (incf var))))) ;=> foo ; nil ; nil

(set 'bar (foo))

(loop :repeat 5 :collect (funcall bar)) ;=> (1 2 3 4 5)

上記のコードを眺めると復帰するのに色々と手間が掛っているように見えますが、新しいシンボルオブジェクトに置き換える必要があるので、良く考えれば妥当であることが理解できるでしょう。

なお処理系によっては、SBCLのように宣言を取り消せるものもあります。

(setf (sb-int:info :variable :kind 'var) :unknown)
;=>  :unknown

しかし、宣言を取り消しても、上記fooの再コンパイルは必要のようです。
ちょっと不思議ですが、規格にない機能なのでしょうがないかなとは思います。

仕組み

Common Lispなどの古典的なLispでは、ソースコードはリストとシンボル/アトムで表現されています。
字面上の表現だけに囚われず、生きたオブジェクトで構成されていると捉えれば、不可解と思われる挙動もすっきり理解できることも多いです。

参考: #:g1: Common Lispの変数の種類と振舞い


HTML generated by 3bmd in LispWorks 7.0.0

Older entries (2002 remaining)