#:g1: frontpage

 

Emacs Lispのcl-fletは使いものにならないなんてことはない

Posted 2016-12-10 10:25:44 GMT

(この記事はEmacs Advent Calender 2016の10日目の記事です。)

10日目が空席で寂しかったので書きました。

さて、Emacs Lispで、Common Lisp互換的なことを実現するcl.elが色々整理され、cl-lib.elとなり、過去との互換性がなくなったりしているようです。
中でもfletの仕様がCommon Lisp的には正しいものになったので、これまでCommon Lispの挙動とは全く違った謂わばミスフィーチャーを活用していた方々には不評のようです。
その期待される機能ですが、fletのボディのスコープの範囲で呼び出される関数が浸透的にシャドウされるというものです。
単にシャドウされている訳ではなく、呼び出した関数がシャドウされる名前を使っていたら、それも影響を受けます。
これによって標準関数など既存の関数を局所的に変更できるのが便利、とされています。

(defun foo (&rest args)
  (apply #'list args))

;; cl-lib (cl-flet ((list (&rest args) (cons :junk args))) (foo 1 2 3 4)) ;=> (1 2 3 4)

;; cl.el (flet ((list (&rest args) (cons :junk args))) (foo 1 2 3 4)) ;=> (:junk 1 2 3 4)

なぜこういう仕様なのかは、cl.elのfletの実装を眺めると分かりますが、シンボルに付いた関数を局所的に挿し替えてしまっているからです。
Emacsにはそもそもローカル関数はないため自作したものの失敗しちゃった感じになっています。

翻って、cl-libのcl-fletでは、コードウォーキングをして関数のスコープがレキシカルになるように、この問題についてはきちんと対処しています。
ですので、このような例もきちんと動きます。

(funcall (cl-flet ((zot (x) x)) #'zot) 42)
;=> 42

cl.elの方は、スコープから抜けたら変数の寿命が切れるので未定義呼び出しになってしまいます。

(funcall (flet ((zot (x) x)) #'zot) 42)
;>> error: (void-function zot)

壊れたfletが欲しい場合には

壊れたfletの仕様を実現するものには、cl-letfが使えます。cl-lib.elに含まれているので外部のライブラリを持ってくる必要もありません。
ちなみに、このcl-letfですが、Common Lispには同様の構文は存在しません。
Lisp Machine Lispのletfを拾ってきたのだと思いますが、何故Common Lispとされているのか謎の一つです。 (参考にしたCommon Lispの処理系が持っていたとか?)

どんな構文かというと、setflet版ともいうべきもので、スコープを抜けたら元に戻ります。

コードはこんな感じになります。

(letf (((symbol-function 'list) (lambda (&rest args)
                                  (cons :junk args))))
  (foo 1 2 3 4))
;=> (:junk 1 2 3 4)

上記の場合、局所的にlistシンボルの関数を挿し替えています。
構文上でもその影響範囲は大体察することができますし、壊れたfletよりずっと良いと思います。

なお、Emacs Lispにせよ、Common Lispにせよletfの効率の良い実現は難しいとされていますが、それは、Lisp Machine Lisp(Lispマシン)にはメモリ参照に特別な仕組みがあり、参照レベルで動作を挿し替えることが可能だったという事情があり、マクロで小細工するレベルでは不可能なことが本物では可能です。

まとめ

cl.elのfletの動作をみて、「Emacs Lispの関数スコープはダイナミックなんだ!」と感心する人さえ出ていますが、まったくの間違いです。 エディタのカスタマイズでは、ローカル構文でのカジュアルな差し替えは歓迎されますが、プログラムを書くとしたら使うべき機能ではないと思います。

また、cl-lib.elのcl-fletは良くやっているのですから褒めるべきです。


HTML generated by 3bmd in LispWorks 7.0.0

レトロLisp探検: MDL

Posted 2016-12-10 07:17:19 GMT

方言の系統

MDL

主要開発者

Gerald Sussman、Carl Hewitt、Chris Reev、Dave Cressy

登場時期

1970年頃

特徴

1970年代としては画期的で実験的な機能を多数持ち、後続のLispに長所が取り入れられることになった。

後続への影響

Lisp Machine Lisp、Common Lispのラムダリストキーワード、述語に?を付ける命名規約等

概要

MDLは、Muddleと読むとのことですが、Plannerの成果を踏まえた処理系で、ダイナミック・モデリングなシステムを構築するために作られたのが始めのようです。

  • Lispの対話性を持つ
  • 型指定が可能
  • リスト、文字列、ベクタ、一様ベクタ等多数の型を持ち、ユーザー定義も可能
  • コンパイラが優秀。ネイティブコンパイラで当時のFORTRAN、COBOL並のスピードは出る
  • パッケージシステムを持つ
  • 拡張されたラムダリスト

等々の特徴があります。

MDLのマニュアルを眺めるとその後のMIT系Lisp、特にLisp Machine Lispに甚大な影響を与えているようで、

  • リストのスプライシング記法(!を使う)
  • コルーチン
  • oblistを管理するblockを発展させたパッケージシステム
  • 拡張されたラムダリスト(“OPTIONAL”、“TUPLE”、“AUX”(“TUPLE”がCommon Lispでいう&rest))
  • オプショナルな型宣言

などはそのままに近い形で輸入されている感じです。
型宣言などは、どちらかといえばCommon Lispに強く影響が現れているかもしれません。

スプライシングは、Common Lispのように、バッククォートと、コンマの対ではなく、!を使った記法になっています。
式(form)とデータ型のリスト(list)は別々の為、クォートはあまり出てきません

<SET FOO (1 2 3)>

(!.FOO 1 2 3) ;=> (1 2 3 1 2 3)

見た目の特徴としては、<>で囲まれているのがよくあるLispとは違います。

<DEFINE FIB (N "OPTIONAL" (A1 1) (A2 0))
  #DECL ((N A1 A0) FIX)
  <COND (<0? .N> 0)
        (<1? .N> .A1)
        (ELSE <L? .N 2>
              <FIB <- .N 1> <+ .A1 .A2> .A1>)>>

<FIB 10> => 55

<>が式で()がリスト、.は、<lval ...>を表わすリーダーマクロで、ローカル変数を取得します(,は大域変数)

データ型は、#なんとか (...)と書かれますが、関数もデータ型に含まれていて、 #FUNCTION ((X) <* .X .X>) のように書け無名関数として使えます

<#FUNCTION ((X) <* .X .X>) 9>
=> 81

以上、概略にもなっていませんが、非常に面白い機能が満載なので興味を持った方は是非マニュアルを眺めてみてください!
以下、目についた所を紹介します。

細かいけど面白い所

整数(FIX)がコレクションの要素にアクセッサになる

arcと同じですが、

<1 (1 2 3 4)>
=> 1

という挙動です。MDLは、1オリジンなので、LENGTHの結果で最後の要素にアクセスしたりするようです。

<<LENGTH .FOO> .FOO>
=> ...

;でコメントアウトするのは直後の一つの式

Schemeの#;と同じですが、

FOO-BAR-BAZ ;"foo
bar
baz"

のように書きます。複数行に渡って書く場合は、文字列にするという慣例があるようです。

型によって評価規則を変えられる

  • EVALTYPE
  • APPLYTYPE
  • PRINTTYPE

というものが用意されていますが、これらを使って、型に応じて印字結果を変えたり(Common Lispのプリティプリントのような)、評価規則を変えたりできます。
マニュアルの例では、LISTをFORMと同じくすることで、普通のLispのように丸括弧で評価できるようにする方法が紹介されています。

<EVALTYPE LIST FORM>
<EVALTYPE ATOM ,LVAL>

(+ 42 42) ;=> 84

上では関数のみを受け付けますが、スペシャルフォームの扱いもどうにかできるのか興味があるところです。

文字列補間がある

MDLの場合、文字列中の%の直後の式が評価され合体されます。
マクロを書く場合に使う例が載っていますが、Common Lispで書けば文字列で書いて、read-from-stringで式に変換する所です。

<DEFMAC INC (ATM "OPTIONAL" (N 1)
    #DECL ((VALUE) FORM (ATM) ATOM (N) <OR FIX FLOAT>)
    <FORM SET .ATM <FORM + <FORM LVAL .ATM> .N>>>

<DEFMAC INC (ATM "OPTIONAL" (N 1) #DECL ((VALUE) FORM (ATM) ATOM (N) <OR FIX FLOAT>) <PARSE "<SET %.ATM <+ %.ATM %.N>>">>

体験

PDP-10上で稼動する、MDLは、TOPS-10のエミュレータ上で動かすことができます。(simh、KLH等)。公開TOPS-20サイトを利用してみるのも良いでしょう。

これらのサイトで遊べるZorkはMDLで書かれているオリジナルです。

実行コマンドは、

mdl105

(です105はバージョンらしい)

まとめ

MDLはZorkが書かれたことで有名ですが、言語としては、MDLの系統がそのまま引き継がれることはなかったものの、Lisp Machine LispからCommon Lispに多大な影響を及ぼしています。
1970年代前半のMIT系Lispは時代を20年は先取りしてますね!!
是非マニュアルを眺めてみてください。

参考文献


HTML generated by 3bmd in LispWorks 7.0.0

Lisp的なものとは

Posted 2016-12-09 15:48:21 GMT

(この記事はLisp Advent Calender 2016の10日目の記事です。)

今年でLispアドベントカレンダー参加も4年目になりました。
記事の内容には全然関係ないですが、なんとなくLisp系アドベントカレンダーを纏めてみました。

Lisp的なものとは

さて、ここ数年漠然とLisp的なものとは何かを折につけ考えたりしています。

そもそも何を持ってLispとするかという話もままありますが、S式でコードを書かなければLispではないとか、S式だったらLispだとか、本物のS式でなければLispとは言えない(本物のS式とは?)等、様々な意見があります。

そこで私も勝手にLisp的なものを並べてみることにしました。

メタプログラミングできる/できない

LISPの誕生時から、M式(メタ表現)でS式(プログラムデータ)を操作する訳ですからメタプログラミングできることはLispの根本ではないかと思います。

M式から変換したS式を超循環評価器に与えるとしていたものも、その後に最初からS式で書いてしまえということで、プログラマもこのevalの循環の中に参加することになります。

メタプログラミングできないものでLispと呼ばれるものはあまり知らないですね(mocklisp位でしょうか)

プログラムがS式である/ない

最近良く言われるHomoiconicityというものですが、Lispの場合は、プログラムをS式というリスト構造で表現し、言語がリストをサポートすることで、直截的なメタプログラミングが可能です。

リスト操作の言語で、プログラムのソースツリーがリストで表現されている訳ですから、操作が非常に手軽で、

“Lisp is a programmable programming language. - John Foderaro”

と言われる所以でもあります。可能であるだけでなく、手軽でなければ、ここまで言いたくもならない気がします。

evalにプログラムを表現した文字列を与えることができる言語も多いですが、リスト表現と違ってプログラム構造を扱うことが面倒だと思います。

この手軽さを活かした代表的なものがLispのマクロですが、マクロに限らず、プログラム生成/変換は非常に簡単ですし、生成したプログラムツリーをevalすればプログラムが動きます。

さらに、M式→S式変換のように、リストを直接操作するのではなしに、なんらかの文法をS式に変換する方式も沢山ありますし、DylanのようにS式は経由しないもののLispをバックグラウンドに持ち骨格はLispだなあというものもあります。

また、S式でLisp以外の言語を記述し、Lispのコード操作の知見を活かすことも良く行われています。

以上、メタプログラミングの土台としてS式利用する方法は様々ですが、操作対象と、操作する側が同一の言語であればLisp的だなと思います。

GCがある/ない

1990年代前半までGCといえばLispでしたが、その後GCを持つ言語が普及し、あまりLispの特徴という感じでもなくなってしまいました。
Lisp界でも実用的な理由によってメモリの特定の領域をGCから外したり、GCを切ったりする機能があったりします。
まあでもLisp系言語といえばGCが備わっている感じですね。

対話式か/否か

ここでいう対話式とは、Lispのリスナー(REPL)のように、ランタイムが起動していて、プログラムを打ち込めば、即実行でき、評価した結果は積み重なって行くものを言うことにします。

プログラムを書く→コンパイル→実行(以下、バッチ方式と呼ぶ)を素早く行なうことでも、対話的なプログラミングは可能ですし、最近のIDEではそのような機能を持つものは多いと思いますが、飽く迄、サイクルを早く回しているだけのことが多いかと思います。
言語が対話機能をサポートしていない場合、実行環境によってセマンティクスが微妙に違ってきたり、対話環境では特定の機能が使えない等の制限があります。

Common Lispなどは、この問題に正面から取り組んだ言語だと思いますが、対話方式でもバッチ方式でも(インタプリタでもコンパイラでも)同一のセマンティクスを得るために、それなりの対価を払っています(最適化が難しい/諦める等)
しかし、そのお蔭で、プログラムのリード時、マクロ展開時、コンパイル時、ロード時、実行時(対話時)、様々なフェイズで、言語の機能を制限されることなくフルに活用できるようになっています。

ちなみに、Schemeでマクロの方式やREPLをどうするかという議論が盛んにされることがありますが(R6RSなど)、処理系との対話について考える風土の外から眺めれば、一体なんのこっちゃという感じに見える気がしないでもありません。

とりとめない感じになりましたが、やはりLispといえば、どこまで詰めるかはおいて対話重視でしょう。

処理系内に全てを含むことを指向するか/否か

ここでいう、処理系内に全てを含む、というのは、LispやSmalltalkでいうイメージ指向ということですが、簡単にいうなら、LispがユーザーのOSを指向するかどうか、ということになります。
OSは機械とユーザーとのインターフェイスを司り、大抵の言語は、プログラムを書いたり対話したりする一ツールですが、Lispは対話性を追求する中で、1970年代後半には、OSそのものとなりました。
(OSがない時代の対話機能をもつ言語処理系でもそんな状況は起こりますが、それはおいておきます)

対話環境がOSまで発展してしまう例(Lisp/Smalltalk)と、ツールの集合体として洗練されていく例の二つに大別できると思いますが、現在は後者が主流です。

どこのOSのレイヤーを覗いてもLisp、上から下までLispで、どのレベルにおいてもLispで書けるというのはLisp環境の理想だったようで、古参のLisperは未だにLispマシンの夢を見ている人が散見されます。

とはいえ、この辺りは、全Lisp方言に共通という訳ではなく、イメージ指向でOSと化していた、Interlisp-D、Zetalispから、中間的なCommon Lisp、よりツールの方に寄ったScheme、etcと様々な形態があります。

むすび

自分の考えるLisp的なものをまとめてみました。

Lispは、プログラム可能なプログラミング言語であり、ユーザーとのインターフェイスでもあり、コンピューターとの対話のどのレイヤーでも統一的な表現でメタプログラミングも容易で、それぞれの特長は相互に支え合っているが、しかし、現実的には、バラバラのツールとしても使えるもの、ではないでしょうか。

ユーザーがプログラミング言語をプログラミングしていく風土は、言語設計者とユーザーの垣根を取り払い、言語設計者のような視点を持つ人も多いですが、Lispというものがユーザーの発想に及ぼす影響も結構あるのかなと思ったりします。

未来のLisp

以上、Lisp的なものを考えてみましたが、これまでLispというものが含む環境で最大の単位はLispマシンに代表されるOSであったかと思います。

現在OSは相互にインターネットに接続される世界となっていますが、Lisp的思考を更に発展させるならば、インターネットがLispになるようなものを考えてみても面白いかなと思っています。
例えば、世界で単一イメージのLispがあり、ユーザーはなんらかの一つの単位であるような環境などですが、ABCL(Actor-Based Concurrent Language)がインターネット規模に拡大したようなものかもしれません。
ちょっと考えてもセキュリティ面で大変そうですが、実際あったら、どういうことになるのか興味があります。


HTML generated by 3bmd in LispWorks 7.0.0

日本のLisp系アドベントカレンダーまとめ

Posted 2016-12-09 10:26:01 GMT

2009-2010

  • LISP365 ※アドベントカレンダーにインスパイアされ365日記事投稿を続けてしまった例

2011

2012

2013

2014

2015

2016


HTML generated by 3bmd in LispWorks 7.0.0

レトロLisp探検: INTERLISP

Posted 2016-12-08 16:22:41 GMT

方言の系統

INTERLISP

主要開発者

Danny Bobrow、Warren Teitelman

登場時期

  • SDS 940 LISP (1967)
  • BBN-LISP (1970)
  • CLISP (1971)
  • INTERLISP (1973)
  • Interlisp-D (1981)

特徴

元祖IDEともいうべき開発環境

後続への影響

直系の子孫は途絶えたものの言語機能の一部は後続のLispに受け継がれる。 - Common LispのLOOP、デバッガー等

また対話性がコマンドシェルの機能として受け継がれたものもある。 - cshのコマンドヒストリー実行機能等

概要

INTERLISPは、MACLISPと1970年代から80年代にかけてLISP界の人気を二分した方言です。
しかし、1984年にMACLISP系がまとめられたCommon Lispが登場すると、Lisp界をCommon Lispが席巻してしまい、その後、1990年代中盤には仮想LispマシンのMedleyを最後にINTERLISPの流れはほぼ途絶えてしまうことになりました。
しかし、LispにおいてINTERLISPが開拓したものは沢山あります。

Common Lispは乱立する方言を統一するため開発された、という説が広まっていると思いますが、実際の所は、当初DARPAの予算を獲得するためにMIT等人工知能系研究機関で広く利用されていたMACLISP系をまとめようというものでした(DARPAは派閥に分かれて似たようなものを作っている所にそれぞれ投資するようなことはしたくなかったため)。

このためINTERLISP色はCommon Lispには殆ど感じられない結果になりましたが、LOOPやデバッガ、対話機能、イメージ指向等にその影響は残っています。

歴史

INTERLISP系統はPDP-1上のLISPが発展したSDS 940 LISPを元に改良を重ね、TOPS-10もサポートするようになりBBN-LISPとして知られるようになります。

BBN-LISPは初期から構造エディタをLispに内蔵していましたが、対話的開発環境の探求が推し進められ、Warren Teitelma氏を中心に高度な対話機能が研究/実装され、CLISP(Conversational Lisp)という形で世に発表されます。

DWIM(Do What I Mean)機能というスペル修正機能や、それを応用した中置記法の仕組み、BREAKパッケージに代表されるプログラムを途中で止めて観察/デバッグする等々、全てLisp内で対話的に実行できる環境は本当に画期的だったようです。

BBN-LISPはその後Xeroxに移り、INTERLISPとなり、1980年代にはXeroxのLispマシンである通称Dマシン上で稼動するInterlisp-Dへと進化します。

しかし、その後のCommon Lispの擡頭によりInterlisp-DでもCommon LispをサポートするようになるなどINTERLISP方言は急激に下火になって行きます。

体験

PDP-10上で稼動する、INTERLISP-10は、TOPS-10のエミュレータ上で動かすことができます。(simh、KLH等)。公開TOPS-20サイトを利用してみるのも良いでしょう。

また、Interlisp-Dは、仮想Lispマシン環境のMedleyが古いLinux上で動かすことが可能です。

文献を読むだけでなく、実際に構造エディタを利用してコードを書いてみると色々なことが発見できますのでお勧めです!

まとめ

INTERLISPについては非常に多くのトピックがあるので、纏めるのが非常に難しいため適当に端折りました。
言語そのもの、開発環境、文化etcは、MACLISP系統と同じ位色々なことがあるのですが、INTERLISP系でレトロコンピューティング的な探索をしている方は少ないようです(残念ながら私は一人も知りません)。

INTERLISPについては、去年エディタを調べていた時に色々書いていたようなので、ご興味あれば、こちらもどうぞ。

他にも散発的に適当なことを書いてます

日本語で読める書籍としては、統合化プログラミング環境―Smalltalk‐80とInterlisp‐Dソフトウェア開発支援環境―UNIXからAIアプローチまでが割とまとまっているかなと思います(特に前者)

参考文献


HTML generated by 3bmd in LispWorks 7.0.0

レトロLisp探検: KLISP

Posted 2016-12-08 15:00:59 GMT

方言の系統

  • LISP 1.5

主要開発者

登場時期

  • 1967

特徴

ユーザーがプログラムをM式で記述する

後続への影響

日本のコミュニティに書籍経由で影響を与える

概要

KLISPは1967年から中西正和氏(1943-2002)が開発していたLISP処理系です。
中西先生はLISPの書籍を出版されていますが、書籍の中ではM式で記述されているのが特徴となっていました。
現在、Wikipediaなどで確認しても、M式で記述されたLISPは実現しなかった、というのが通説になっていますので、書籍中に記述されたM式を眺めるに、実装からは離れたイデア界の記述のように感じてしまいますが、さにあらずで、中西先生は実際にM式で記述するKLISPを1967年に開発し、後に開発する処理系もこの流れを踏襲していたのでした。

KLISPの構成ですが、GLISPというトランスレータがあり、これがM式→S式と変換します。
当初GLISP自体は全て機械語で書かれていたとのことです。

S式での記述は、

  • カッコが多いので複雑
  • 全てS式のみで構成されていて読解しづらい

という主張がベースですが、KLISPに先行して存在した、A-Languageというものの問題点である、

  • ユーザーの自由度が高すぎて処理系が処理するのも人が読むのも骨が折れる
  • S式から離れ過ぎてLispの特長を活かせなくなる(メタプログラミング等)

という所を鑑みて、落とし所としてM式を採用としたようです。
ちなみに、A-Languageというものが調べきれていないのですが、S式以外の文法を採用ということであれば、ALGOL文法を採用したLISP 2もKLISPに先行しています。

また、括弧の多さもM式もS式も変わらないんですよねえ。しかし後世の人も同様の感覚なので正確な数が問題なのではなく、視覚的な括弧の多さとも言うべきものがあるのかもしれません。

さて、このM式を受け付けるGLISPですが、マッカーシー先生のオリジナルのM式の記述で利用されている文字は、そのまま使えないので若干のアレンジが加えられています。

  • condは、->
  • quoteは、“'”引用符で囲む

となっていますが、M式だと大文字の記述はS式のシンボルを表わすところで大文字しか使えないため引用符を使う必要があったのでしょう。

quoteは、“'”引用符で囲むとなっていますが、'FOO'のような表記なのか、'FOOなのかは判然としません。
後の中西先生のAPPLE LISPでは、GLISPとほぼ同じMETA LISPがありますが、こちらは、'FOOという形式です。

実際のコードはどんな感じになるかというと、

COMB1[A;X;Y]=
        [NULL[Y]->CONS[APPEND[A;
                CONS[X;Y];NIL];
        T->CONS[APPEND[A;CONS[X;Y]];
                X;CDR[Y]]]];
COMB2[X;Y]=[NULL[Y]->NIL;
        T->APPEND[COMB1[NIL;X;CAR[Y]];
        COMB2[X;CDR[Y]]]];
PERM[X]=[NULL[Y]->CONS[X;NIL];
        T->COMB2[CAR[X];PEAM[CDR[X]]]].

という感じです。文献を眺めても書いてありませんが、どうやら'.'で全体の終了のようです。

全体的な印象としては、大文字しか使えない(もしくは区別のない)M式という印象。
M式中のS式も(A B C ...)と書くようです。

また、GLISPは単なるトランスレータなので、出力をKLISP以外の処理系で活用することもできたようです。

まとめ

中西先生のプログラミングスタイルは実践的で、限られた計算機資源の中で最大の効率を上げるスタイルだったようですが、1967年という時期にこのようなシステムを作り上げている手腕はまさに天才的だと思います。

中西先生は、竹内郁雄先生と同世代ですが、2000年に亡くなったためか、今現在ネット上で知ることができるLisp系の文献というものが多くないようです。
1960年代からの中西先生の功績がまとめられ、ネット上でも広く知られるようになることを願っています。

参考文献


HTML generated by 3bmd in LispWorks 7.0.0

レトロLisp探検: MLISP

Posted 2016-12-07 15:01:00 GMT

方言の系統

  • LISP 1.5

主要開発者

Horace J. Enea

登場時期

  • 1968年

特徴

ALGOL風の構文を持つLISPの試み

後続への影響

LISPの中置系構文を持つ処理系(INTERLISPのCLISPや、CGOL等)

概要

MLISPはALGOL風の構文をLISPに導入しようという処理系です。
同じ試みには、LISP 2がありますが、完成に至らなかったLISP 2とは違って実際に稼動していた様子。
LISP 2と同じトランスレータ方式ですが、LISP 2がレキシカルスコープの所、MLISPはLISP 1.6へのトランスレートなのでダイナミックスコープです。

MLISPのMが気になりますが、やはりどうもMETAのMのようです。
LISP 1.5でM-S変換が、M式→S式の所を、MLISPでは、ALGOL構文→S式 ということなのかもしれません。

MLISPの初期のコードは下記のような感じです。

% THIS PROGRAM IS AN EXAMPLE %

BEGIN NEW A,B,C,D,,I,J,L,ST; MACRO OFF "VERBOS(NIL)"; C:=(6.7*4)/3 + 2**4; A:=C CONS D; B:=<1,2,3>; B:=C:=CDR(A); B:='B; C:='( B (C.A));

PRINT (ST:="AB// 7"@DBQUOTE); PRINTSTR(ST SUBSTR<3,2>); C:=MAKATOM("ASDF//");

LAST:=#L: IF ¬L THEN NIL ELSE IF ¬CDR(L) THEN CAR(L) ELSE LAST(CDR(L));

REVERSE:=#L: BEGIN NEW I,J; FOR I I IN L DO IF ATOM(I) THEN J:=I ¢ J ELSE J:= REVERSE(I) ¢ J; RETURN(J); END;

¢cons@append¬notとなります。
代入は:=ですが、後にとなったようです。

初期のMLISPには文字列型がLISP 1.5と同様存在しないようで、"ABC"'(A B C)へと展開されるようです。

また、#A,B,C:(lambda (a b c) ...)を表します。
なかなか良いなと思っていたのですが、後期には、lambda(a,b,c);...;と何の変哲もないものに変更されてしまい、ちょっと残念。

begin〜endは、ブロックになっていて途中からの脱出はreturnで抜けます。

繰り返しは、forcollectを使いますが、Common LispのLOOP、INTERLISPのFOR並に高機能で、Common Lispの

(loop :for i :from 0 :to 10 :collect i)
;=> (0 1 2 3 4 5 6 7 8 9 10) 

のようなものは、

for i in 0 to 10 collect fn<i>;

と書けるようです。
INTERLISPのCLISPの参考文献にMLISPがあるので、INTERLISPのCLISPはMLISPのforに影響を受けているらしいです。
そうであるとすると、Common Lispのloopは、MLISP → INTERLISP(CLISP) → Lisp Machine Lisp → Common Lispという流れになります。
LISP 2でもリッチな繰り返し構文が存在しているので、ALGOL → MLISP → INTERLISP(CLISP) → Lisp Machine Lisp → Common Lispという流れなのかもしれません(ALGOLに疎くて詳細不明)
なんにしろloopの構文がALGOLっぽい由来になっていそうです。

コード例

さて、MLISPのコードの見た目ですが、Common Lispで、

(defun fib (n a1 a2)
  (cond ((= 0 n) a2)
        ((= 1 n) a1)
        (T (fib (- n 1) (+ a1 a2) a1))))

のようなものは、

expr fib (n, a1, a2)
   if n=0 then a2
   also n=1 a1
   else fib(fib(n-1), a1+a2, a1);

と書けるようです。
else ifではなくalsoと書く所が面白い。
しかしこれもまた後期には、なんの変哲もない、else ifになってしまうようです。

Decomposition Assignment

MLISPでは、Common Lispの(cadr (caddr (car L))))L[1,3,2]と書くことができたりするのですが、さらに込み入ったことが可能な代入構文があり、MIT系でいうリストのdestructuringが可能です。

パターンマッチというものが盛んに研究されていた時代ということもあるようですが、1969年辺りということを考えるとかなり時代の先を行っていたのではないでしょうか。
例えば、

‘(_ (x y) z _) ←⊗ '(A B (C D) E F)

x ⇒ 'c y ⇒ 'd z ⇒ 'e ※ _はお馴染のプレイスホルダー

のような記法が可能です。
なお変数はダイナミックである必要があります(この時代にはありがち)

現在のCommon Lispのdestructuring-bindのようなものは、MITで1979年辺りに流行しますが、MIT系Lispの先を行くこと10年という所です。

MLISP 2

MLISPは更に進化し続け、1973年にはMLISP2が登場します。
MLISPとの完全な互換性はないものの、更に汎用的な束縛構文、ジェネレータ、さらにはバックトラック機構が追加されていて、モダンだったり謎だったりする機能が山盛りですので、興味のある方は探ってみることをお勧めします!

体験

SAILDARTではウェブブラウザから操作できるエミュレータを公開していますが、MLISP、MLISP2が起動できます。
しかしどうも起動はするものの完全な環境ではないようで機能してはいないように見えます。

simh等を利用したローカル動く環境も準備できそうですが現在調査中です。

まとめ

今回は、MLISPを紹介してみました。
色々あり過ぎて紹介し切れないので、そのうちまた機会があれば、ちょくちょくMLISPについて調べて書いてみたいと思います。
時代を先取りし過ぎた感のある処理系ですが、現在Kotlinや、Swiftあたりで導入され始めた感じの流行りの構文が40年前位にはある程度は実現されていたと思うと面白いですね。

参考文献


HTML generated by 3bmd in LispWorks 7.0.0

レトロLisp探検: MACLISP

Posted 2016-12-06 16:54:06 GMT

方言の系統

  • MACLISP

主要開発者

Richard Greenbratt、Jon L White、Guy L. Steel, Jr.、Kent Pitman

登場時期

  • 1965年

特徴

実用性を追求した処理系。
1980年代まで人工知能研究に於て基盤となって活躍した。
現在Lisp的と思っていることの多くがMACLISPで導入されたものであることは多い。

後続への影響

Common Lisp、Lisp Machine Lisp、Franz Lisp等、MACLISP系の大元

概要

MACLISPは現在広く利用されている、Common Lisp、Schemeの大元の流れを作った方言です。
極初期のことを記述した文献が残っていないようなので、詳細は不明なのですが、1965年にPDP-6がMITに納入され、その直後からRichard Greenblatt氏が実装を開始しPDP-6 LISPとして実現します。そして1966年にはもうAIラボ内で普通に活用されるようになっていたようです。
そして、1968年あたりにPDP-6の後継機であるPDP-10が導入される頃には、MACLISPという名称に変更されます。

この1968年あたりから主要な開発者はJon L White氏が担当していたようで、更新作業の記録(LISP NEWS)が残っています。
1972年頃には、Guy L. Steel, Jr.氏が参加するようになり色々な機能を加えています。

LISP NEWSから面白そうな所を抜粋すると

1969-03-01 DEFUNとDOの導入

1)"DEFUN" IS AN FSUBR USED TO DEFINE
FUNCTIONS. EXAMPLES ARE
    (DEFUN ONECONS (X) (CONS 1 X))
WHICH IS EQUIVALENT TO 
    (DEFPROP ONECONS 
        (LAMBDA (X) (CONS 1 X)
     EXPR)

AND (DEFUN SMASH FEXPR (L) (RPLACD L NIL)) IS EQUIVALENT TO (DEFPROP SMASH (LAMBDA (L) (RPLACD L NIL)) FEXPR) THE NOVEL FEATURE OF "DEFUN" IS THAT ONE NEED NOT BE SO CONCERNED WITH BALANCING PARENTHESES AT THE VERY END OF THE FUNCTION DEFINITION, SINCE THE TYPE FLAG MAY BE OMITTED IF IT IS "EXPR", AND APPEARS NEAR THE FRONT OF THE "DEFUN" LIST IF IT IS SOME OTHER. ALSO, THE "LAMBDA" NEED NOT BE DIRECTLY INSERTED.

2)A FAST "DO" SIMILAR TO THE FORTRAN DO FEATURE NOW EXISTS. THE SYNTAX IS (DO ATOM INITIALVALUE STEPFUN ENDTEST STATEMENT1 . . . STATEMENTN) WHERE "ATOM" IS THE INDEX VARIABLE OF THE LOOP, WHICH IS INITIALLY SET TO THE EVALUATION OF "INITIALVALUE", AND IS RESET EACH PASS THROUGH THE LOOP TO THE EVALUATION OF "STEPFUN". "STATEMENT1" TO "STATEMENTN" COMPRISE A REGULAR PROG BODY (EXCEPTING THE LIST OF PROG VARIABLES) WHICH IS EXECUTED REPEATEDLY UNTIL "ENDTEST" EVALUATES TO NON-NIL. FOR EXAMPLE, (DO I 0 (ADD1 I) (EQ I 400) (COND ((NULL (A I)) (GO B))) (PRINT (A I)) B (SETQ TOTAL (PLUS TOTAL (A I))))

1973-09-15 PROGNとFUNCALLの導入

LISP NOW HAS TWO MORE FUNCTIONS:
[1] PROGN, AN LSUBR OF 0 OR MORE ARGS, WHICH (ODDLY ENOUGH)
    RETURNS ITS LAST ARGUMENT (NIL IF NO ARGS).
[2] FUNCALL, AN LSUBR OF 1 OR MORE ARGS. (FUNCALL F X1 X2 ... XN)
    CALLS THE FUNCTION F WITH ARGUMENTS X1, X2, ... XN.
    THIS IS USEFUL IN SITUATIONS WHERE ONE WANTS TO EVALUATE
    SOME EXPRESSION TO OBTAIN THE FUNCTION. THUS
    (FUNCALL (CAR X) A B C) IS SIMILAR TO ((CAR X) A B C)
    BUT LOOKS NICER. ITS PRIMARY USE IS IN THE CASE WHERE
    SOME VARIABLE HAS A FUNCTION AS ITS VALUE, BUT MAYBE ALSO
    HAS A FUNCTION PROPERTY. EXAMPLE:
    ((LAMBDA (CAR) (CAR X)) 'FOO)
    RETURNS THE RESULT OF APPLYING THE FUNCTION CAR TO THE
    VALUE OF X; HOWEVER
    ((LAMBDA (CAR) (FUNCALL CAR X)) 'FOO)
    RETURNS THE RESULT OF APPLYING THE FUNCTION FOO TO X.

等々面白い記述が多いです。

上記funcallについては所謂Lisp-2はfuncallが必須と思っている方が多いので、逆にfuncallがそれまで存在しなかったのかと不思議に思うかもしれませんが、Lisp処理系では関数ポジションがどのように評価されるかでも挙動が違ってきます。
Common Lispは一度も評価されませんので、

((LAMBDA (CAR) (CAR X)) 'FOO)

とあった場合は、CARの関数を拾うことになります。仮にCARの関数が未定義であった場合には未定義のエラーとなります。

MACLISPの場合は、アトムが出てくるまで評価します。上記の場合、

((LAMBDA (CAR) (CAR X)) 'FOO) => ('FOO X) => (FOO X)

となりFOOを呼び出すことになります。このような仕様なので、

((LAMBDA (CAR) (CAR X)) 'FOO)

のような名前の被りが発生してしまうケースは

((LAMBDA (CAR) (FUNCALL CAR X)) 'FOO)

と書くと明解だよねということでfuncallが導入されたのでした。

ちなみにSchemeでは関数ポジションは引数ポジションと同じく一度だけ評価されますね。

その他、Lispの仕様的に大きな決断としては、浅い束縛の採用に大きく舵を切ったことが挙げられると思います。
個人的には、Lispマシンで浅い束縛システムはハードウェアの支援を活用することにより完成の域に達した(見えないポインタ、closure構文、等々)と考えているのですが、その辺りに詳しい方に一度実際どうだったのか伺ってみたいところではあります。

応用の例

MACLISP上に構築された歴史的に有名な応用は枚挙に暇がありません。代表的な所では、

  • SHRDLU
  • MACSYMA

言語だと、

  • LOGO
  • Micro PLANNER
  • SCHEME
  • MDL
  • PLASMA
  • Conniver
  • 3-LISP

等々Lispベースのものは本当に沢山あります。

体験

MACLISPが稼動するITS、TOPS-20ともにsimhや、KLHのエミュレータ上で動きます。

リモートからログイン可能な公開サイトも幾つかあります。

興味のある方は実際に動かしてみましょう。
ちなみにliving computersのものは実機です(恐しや)

まとめ

現在、利用されているLispはMACLISP系か、Clojureのように比較的新しくできた方言に大別できます。
MACLISPの影響を非常に多く残しているのは、Emacs Lispですが、RMSはMACLISPで育ったことが非常に大きいと思います。
Emacs Lispは、Common Lispに由来していると思っている人が多いと思いますが、実際の所は、MACLISPから拾ってきているものが、同じ子孫であるCommon Lispにも存在するということが多いためそのように見える、ということだと思います。

参考文献


HTML generated by 3bmd in LispWorks 7.0.0

レトロLisp探検: LISP 1.6

Posted 2016-12-05 15:00:59 GMT

方言の系統

  • LISP 1.6

主要開発者

  • John Allen、Lynn Quam

登場時期

  • 1966年頃

特徴

1970年代に、MACLISP、INTERLISPに並び活躍していた方言

概要

MITのPDP-6 LISPから派生し、スタンフォード大学のAIラボ(SAIL)で利用された処理系ですが、PDP-6 LISPがMACLISPへと進化したのと同じく、SAILでも独自の進化し、LISP 1.6系で一つの流れを作ります。

面白いのが、LISP 1.5という名称から勝手にバージョンを上げているように見えるところですが、MITのPDP-6 LISPもLISP 1.6と呼ばれることもあったようなので、貰ってきた時からそうだったのかもしれません。 また、ややこしいですが、初期は、PDP-6に実装されたLISP 1.5をLISP 1.6と呼んだようにも見えます。

Lisp 1.6. Operating NoteによるとMITからPDP-6 LISPと貰ってきたのが、1966年あたりのようですが、1968年頃には結構独自の機能が実装されます。
また、MIT/SAILともにPDP-6からPDP-10へ主軸を移し、この頃PDP-6 LISPは、MACLISPとなったようです。

1968年頃までは、MACLISPもLISP 1.6も関数定義には、defpropを使っていました。
こんな感じのコードですが、

(DEFPROP BIGINIT 
 (LAMBDA NIL
  (PROG NIL
    (PUTSYM (VBASE (GET (QUOTE BASE) (QUOTE VALUE)))
        NEGNUM
        POSNUM
        FIXNUM
        FLONUM
        (MINUSP (GET (QUOTE MINUSP) (QUOTE SUBR)))
        (VNOPOI (GET (QUOTE *NOPOINT) (QUOTE VALUE)))))) 
 EXPR)

(DEFPROP CONSCONS
         (LAMBDA (A)
           (COND ((NULL (CDDR A)) (CADR A)) 
                 ((LIST (QUOTE CONS)
                        (CADR A)
                        (CONS (CAR A)
                              (CDDR A))))))
         MACRO)

上記の場合、biginitのプロパティにexprや、macroを設定するということで、exprmacro関数を定義するといった具合です。

この方式は、プロパティを活用しまくっていた、1970年代まで利用可能でしたが、1968年の秋には定義専用の形式として、LISP 1.6では、dedfdmdvが導入されます。
それぞれ、

  • de (define expr)
  • df (define fexpr)
  • dm (define macro)
  • dv (define variable)

となりますが、たまに古いLISPで見られるdeはLISP 1.6が源流ということが分かります。
一方MITでも大体同時期かちょっと遅れて1969年には、defunが導入されます。

また、expr、fexpr、macro以外にもlexprが導入されますが、Schemeで書く所の、

(define mylist (lambda args args))

のように、引数をアトムで書けば、lexprになります。

(de mylist args args)

lexprの定義の中では、(arg n)でn番目の要素を取得できますが、これはPDP-6から受け継いだようです。

また、PDP-6 LISPから受け継いだ所では、

  • mapcarの引数は関数が先
  • *fooという命名規則

があるようです。
map系の引数の順番ですが、LISP 1.5では、(リスト 関数)の順番でした、これを引っくり返して、(関数 リスト)となっています。
可変長引数にしてリストを複数取れるという訳ではないので、当初は単に趣味の問題だったのではないかと思います。

また、*fooという命名規則ですが、fooという関数の補助関数としての*fooという意味ですが、それ単体でも呼び出して使うもの、位のようです。
可変長引数のdifferenceは、*difを下請けに使っていますが、2引数の場合は、*difを使ったりする、という感じです。
似たような命名規則に、fooとその下請けのfoo1foo2というものがありますが、foo1foo2foo内での呼び出し以外では利用しない所に微妙な違いがあるようです。
なお、*fooの命名規則は、1980年代あたりには廃れています。

また、文献にあるちょっと面白いものとしては、(quote foo)(' foo)と書けるようにしたというものがあります。
リーダーマクロはまだ無く、引用記号は単なるアトムですが、以降の文献でも利用されていることもないので、導入してはみたものの、どうやら廃れてしまったようです。
古いEmacs Lispで、バッククォートを、(\ ,foo)`のように書いていたのを思い出します。 

ちなみに、(' foo)としては、受け継がれなかったようですが、後続のUCI LISPでは、quoteを短縮して(q foo)と書くようです。

まとめ

なんといても、LISP 1.6はdeの大元である印象が強いです。
deはMACLISP系であるtao/elis界隈でも、defunより多く使われている位ですが、使われる所では良く使われていたようです。
また、恐らく関連はないと思いますが、二文字に短縮するといえば、gooが思い出されますが、同じ二文字でも微妙に違っています。

Gooの二文字定義構文(一文字も)

  • df (define function)
  • dm (define method)
  • dv (define variable)
  • ds (define syntax)
  • d (define constant variable)

参考文献


HTML generated by 3bmd in LispWorks 7.0.0

レトロLisp探検: KLISP

Posted 2016-12-04 15:00:59 GMT

方言の系統

  • LISP 1.5

主要開発者

登場時期

  • 1967

特徴

ユーザーがプログラムをM式で記述する

後続への影響

日本のコミュニティに書籍経由で影響を与える

概要

KLISPは1967年から中西正和氏(1943-2002)が開発していたLISP処理系です。
中西先生はLISPの書籍を出版されていますが、書籍の中ではM式で記述されているのが特徴となっていました。
現在、Wikipediaなどで確認しても、M式で記述されたLISPは実現しなかった、というのが通説になっていますので、書籍中に記述されたM式を眺めるに、実装からは離れたイデア界の記述のように感じてしまいますが、さにあらずで、中西先生は実際にM式で記述するKLISPを1967年に開発し、後に開発する処理系もこの流れを踏襲していたのでした。

KLISPの構成ですが、GLISPというトランスレータがあり、これがM式→S式と変換します。
当初GLISP自体は全て機械語で書かれていたとのことです。

S式での記述は、

  • カッコが多いので複雑
  • 全てS式のみで構成されていて読解しづらい

という主張がベースですが、KLISPに先行して存在した、A-Languageというものの問題点である、

  • ユーザーの自由度が高すぎて処理系が処理するのも人が読むのも骨が折れる
  • S式から離れ過ぎてLispの特長を活かせなくなる(メタプログラミング等)

という所を鑑みて、落とし所としてM式を採用としたようです。
ちなみに、A-Languageというものが調べきれていないのですが、S式以外の文法を採用ということであれば、ALGOL文法を採用したLISP 2もKLISPに先行しています。

また、括弧の多さもM式もS式も変わらないんですよねえ。しかし後世の人も同様の感覚なので正確な数が問題なのではなく、視覚的な括弧の多さとも言うべきものがあるのかもしれません。

さて、このM式を受け付けるGLISPですが、マッカーシー先生のオリジナルのM式の記述で利用されている文字は、そのまま使えないので若干のアレンジが加えられています。

  • condは、->
  • quoteは、“'”引用符で囲む

となっていますが、M式だと大文字の記述はS式のシンボルを表わすところで大文字しか使えないため引用符を使う必要があったのでしょう。

quoteは、“'”引用符で囲むとなっていますが、'FOO'のような表記なのか、'FOOなのかは判然としません。
後の中西先生のAPPLE LISPでは、GLISPとほぼ同じMETA LISPがありますが、こちらは、'FOOという形式です。

実際のコードはどんな感じになるかというと、

COMB1[A;X;Y]=
        [NULL[Y]->CONS[APPEND[A;
                CONS[X;Y];NIL];
        T->CONS[APPEND[A;CONS[X;Y]];
                X;CDR[Y]]]];
COMB2[X;Y]=[NULL[Y]->NIL;
        T->APPEND[COMB1[NIL;X;CAR[Y]];
        COMB2[X;CDR[Y]]]];
PERM[X]=[NULL[Y]->CONS[X;NIL];
        T->COMB2[CAR[X];PEAM[CDR[X]]]].

という感じです。文献を眺めても書いてありませんが、どうやら'.'で全体の終了のようです。

全体的な印象としては、大文字しか使えない(もしくは区別のない)M式という印象。
M式中のS式も(A B C ...)と書くようです。

また、GLISPは単なるトランスレータなので、出力をKLISP以外の処理系で活用することもできたようです。

まとめ

中西先生のプログラミングスタイルは実践的で、限られた計算機資源の中で最大の効率を上げるスタイルだったようですが、1967年という時期にこのようなシステムを作り上げている手腕はまさに天才的だと思います。

中西先生は、竹内郁雄先生と同世代ですが、2000年に亡くなったためか、今現在ネット上で知ることができるLisp系の文献というものが多くないようです。
1960年代からの中西先生の功績がまとめられ、ネット上でも広く知られるようになることを願っています。

参考文献


HTML generated by 3bmd in LispWorks 7.0.0

Older entries (2030 remaining)