#:g1

Lisp本積読解消: Dylan Programming — 6.4 Macros

Posted 2017-03-10 15:14:00 GMT

今回は、Dylan Programmingのマクロの箇所を読む。

Dylan Programmingは、1996年の出版で、主な対象としている処理系は、Harlequin Dylan。

当初、Harlequin Dylanは、DylanWorksと呼ばれていたらしいが、1.0がリリースされる前に名称変更になったらしい。

Harlequin Dylanは、1998年辺りに正式リリースがされ、1999年にEnterprise Editionがリリースされた後、色々あって、Functional Objectsという会社が扱うことになり、さらに色々あり、Open DylanとしてOSS化された。

以降、現在に至るまで、Open Dylanとして開発が活発になったり停滞したりしている。

本書のPDFは、OpenDylan.orgからダウンロード可能

6.4 Macros

DylanのマクロはLispのように言語を拡張することを目的としていて、扱いも用意であることが解説される。
なお、Dylanの競合は、C++やCなので、Cのマクロとの比較が随時差し挟まれる。

構文マクロには、パタンマッチ・テンプレート方式と、構文を操作する手続き型マクロがあるが、Dylanの場合は、テンプレート方式であることが説明される。
後々、手続き型マクロもサポートする予定だったらしい。なんと、知らなかった。

簡単なパタンマッチでマクロを書く例が示され、次にConstraintsについて説明がある。
Constraintsとは、?foo:expressionのようなパタン変数の後ろの部分で、構文に型が付いているようなもの。
Common Lispでいう&restのようなものは、?var:bodyと書けたりする。

また、サブパタンを書くことも可能で、見通しよく分割できることが示される。
マクロ内で識別子を生成する方法も解説。

次に、マクロの衛生性について解説され、識別子の意図しない捕捉等が起きないことが示される。
衛生性は意図的に破ることが可能で、?=を利用すれば利用される場所の識別子が利用できる。

次に、サブパタンを利用しては書けない例として、生成した識別子がサブパタンでは利用できない例を挙げ、補助マクロを利用する方法を解説する。

結び

最近、中置言語にLisp風のマクロを導入する例が多いが、最近の言語と比べてもDylanのマクロシステムは洗練されているように思える。
Dylanには、Common Lispの開発者が多く参加しているが、構文マクロを普通に使う人達が設計したことがシステムの洗練に大きく寄与したのではないだろうか。

Common Lispを知っている人からすると、Dylanは、まるで中置のCommon Lispで、Common Lispの精神もかなり継承しているのであるが、ブレイクしてくれなかったのが悔まれる。


HTML generated by 3bmd in LispWorks 7.0.0

Lisp本積読解消: プログラマのためのCOMMON LISPガイド — 11 マクロ

Posted 2017-03-07 18:48:22 GMT

今回は、プログラマのためのCOMMON LISPガイドのマクロの章を読む。

1988年邦訳で、原書であるA Programmer's Guide to Common Lispも1987年出版で、CLtL1な時代の本。

「プログラマのための」とあるように、何かしらの言語の経験は前提にしている。

11 マクロ

まず、マクロの効用について説明があり、次に簡単な定義の方法とバッククォートの説明がある。
次に一歩進んで、マクロを書くマクロの説明があり、ネストするバッククォートの書き方(,',の使い方等)が親切に解説されている。
そして、マクロのλリストのデストラクト分配機能について解説がある。
これら一歩進んだ機能を解説する題材として簡単なdefstructを作って行くが、defstructを作るには一通りの知識が必要となるので確かに手頃な題材だと感じた。

章末の練習問題も手頃。後半は、本章で解説した自作のdefstruct:conc-nameや、:constructorを使えるように拡張する等、実際的な問題で面白く為になる。
なお、Common Lispの仕様に忠実に:constructorをサポートするとなると結構大変だが、巻末の解答を眺めるとさすがにそこまでは要求していないらしく、名前を変更する位で良いようだ。

また、:conc-nameや、:constructorサポートするにあたって構造体名の所はリストだけのサポートで良いらしい。
デストラクト分配を使えとあるので、destructuring-bindなし(CLtL1の仕様では存在しない)でどうやるのか色々考えてしまった。
まあ、下請けのマクロを定義するだけで良いのだが……。


HTML generated by 3bmd in LispWorks 7.0.0

LispのloopやformatはUNIXでいうsedやawkである

Posted 2017-03-04 18:38:29 GMT

Common Lispのloopformatは言語内DSLの代表例と考えられていて、その機能の多さからLisperの破天荒さを象徴するものと捉える向きも多い。
しかし、これらの言語内DSLは別にLispらしさの象徴でもないし、寧ろ原理主義者からは昔から嫌われているものの代表例でもある。

formatや繰り返し構文はFortranに由来するが、やりたいことを一つの関数に詰め込んでしまったため、ごちゃごちゃすることになってしまった。

loopは、INTERLISPのCLISPというユーザーフレンドリーな機構の繰り返し機構のforが先祖で、他の言語に親しんだ人のために中置で書ける繰り返し構文をLisp内で実現したものに由来する。

Common Lispのloopformatの内情を知っていれば、Unixのsed、awkのようにシェルからは独立した処理系や、その他オプションがどんどん増える各種コマンドについての歓迎と批判が、非常に良く似た構図であることに気付くと思う。

Unix方面では、原理主義として、一つのコマンドは一つのことしかせず、それをパイプで組み合せることを徹底したPlan9文化(またはdjb氏のユーティリティ群)があり、awkのようなものがさらに発達したPerlのようなものもあり、便利なら良いじゃんということでオプションが沢山付いたGNUのユーティリティがある。

Lisp方面で言えば、Plan9はScheme文化、formatloopのようなものは、sedawkのようなもので、便利なら良いじゃんというのは、大多数のCommon Lispユーザーの立ち位置、という感じになる。 Gaucheのように全体のバランスを考えつつ便利さも失なわないように整えている環境もある。

sedawkがUnixの対話環境の象徴かといえば、そんなことはなく、パイプでコマンドを組み合せていくスタイルこそがUnixの対話環境らしさだろう。
そのような基本があった上で、便利さを追求したり統一性を重視していると思う。

Lisp方面も同じで、formatloopがLispの象徴ということはないのである。

単一の関数やコマンドに詰め込むことの批判として、徹底して小さい部品を組み合せるアプローチも多数出ている。
formatや、loopであれば、コンビネータに機能を分解して、それを組み合せる等々のアプローチがあるし、Plan9ではコマンドは本当に一つのことしかしない。
面白いのがこれらのアプローチが絶対的に使い易いかというと、そうとも言い切れない所である。細かく分割した部品が他のドメインでも上手く使えたりして一石二鳥かというと、そうでもなかったりする。

CLerは割合に現実的なので、loopformatも全機能を熟知した上で使うということもなく、適度なイディオムを利用しているのみである。
また、設計に於いても極力機能をアトミックに分割しようということも無く中庸といった所だ。


HTML generated by 3bmd in LispWorks 7.0.0

Lisp本積読解消: Anatomy of LISP — 3.12 Special Forms and Macros

Posted 2017-03-04 17:35:50 GMT

今回は、Anatomy of LISP / John Allenのマクロの箇所を読む。

LISPの構造として邦訳もある。
邦訳は入手できていないので詳細は不明だが、どうも邦訳の方は端折っている部分が多いらしい。

原著の初版は1978年で、LISP処理系の内部を解説した名著として知られている。
マクロについては、3章のEVALUATION OF LISP EXPRESSIONSの評価器から眺めた解説と、6 6章のTHE DYNAMIC STRUCTURE OF LISPのコンパイラから眺めた解説がある。

3.12 Special Forms and Macros

eval(インタプリタ)から眺めた特殊形式とマクロの解説。
andを実現するのに、まず、関数ではandが作れないことを解説し、evalを拡張する方法を示す(evandを作成)。
次に、同様のことを実現するのにマクロというものを評価器に導入することを説明。

また、可変長引数を実現するのにマクロを使うという手法を解説する。
可変長引数のplusは固定引数(2つ)の*plusに展開されるというもので、マクロそのものを用いてはいないが、現在もCommon Lisp内部ではコンパイラがインライン展開etc.で行なっていることと同じ。

マクロの種類として

  1. Textual Substitusion
  2. Syntax Macro
  3. Computational Macro

三つを取り上げ、単なる文字列の置き換え→構文の置換→サブツリーの変換と強力になることを説明するが、Computational Macroであっても扱うサブツリーに必要な情報が含まれている場合にしか有効でなく、サブツリー以外の場所の情報を必要とする場合や、サブツリーに追加情報が必要な場合にも、うまく立ち回ることができていないことを解説する。

6.18 Macros and Special Forms

コンパイラの方からマクロを眺めた解説。
大体インタプリタの方と同じだが、コンパイラからすると扱いが厄介な特殊形式を排除できることと、マクロはコンパイル時に計算してしまえる部分を多くすることが可能で効率的な処理に寄与することが解説されている。
また、特殊形式が増えるとevalが複雑で重くなるという解説もある。

結び

古代のLISPでは、マクロを説明する際には、FEXPRであったり特殊形式との比較が出てくることが多い。

マクロには、処理系を改造しなければいけない所をユーザーレベルで拡張できるところ増やす、という効用があった。
また、コンパイル時計算との親和性も高く、FEXPRで実現できることも大抵置き換えることが可能なので、FEXPRのようなコンパイルが難しい(と当時は考えられていた)形式も置き換えることとなった。

処理系内部をユーザーに開放しつつ効率が良い道具を提供するというのもLISP処理系の一つの核となる歴史の流れかなあと思う(マクロ、MOP、etc.)


HTML generated by 3bmd in LispWorks 7.0.0

Lisp本積読解消: Realm of Racket — ).3 Racket Is a Metaprogramming Language

Posted 2017-03-03 20:27:07 GMT

今回は、Realm of Racketのマクロの箇所を読む。
Realm of RacketはLand of LispのRacket版のような感じで、Conrad Barski氏も著者の一人。
とはいえLand of Lisp程個性が強くないので物足りない人もいるかもしれない。

マクロの解説は、最後の章(Good-Bye)で

  • ).1 Run Racket Run
  • ).2 Racket Is a Programming Language
  • ).3 Racket Is a Metaprogramming Language
  • ).4 Racket Is a Programming-Language Programming Language

という風にメタプログラミング度を増して解説していくうちの3番目にあたる。

).3 Racket Is a Metaprogramming Language

Racketは他のLisp方言と比べても一番強力かつ表現力が高いという前置きがある。
とはいえ、この章ではそこまで詳しくは解説せず、言語の構文を拡張する方法として簡単なマクロを解説するのみ。
主にRacketで用いられるdefine-syntaxsyntax-rulesを合体したような、define-syntax-ruleを利用するが、それ程つっこんだ解説はしない。

(define-syntax-rule
  (my-and a b)
  (if a b #f))

衛生マクロなので変数捕捉等については特に気にする必要はないが、一応わざと大域変数とマクロ内で利用している変数の名前を衝突させてみたりしても上手く動きますねという解説がある。

次に、パタンマッチの書式の簡単な解説として、lambdaに展開されるletを作成しつつ説明する位で終了。

).4 Racket Is a Programming-Language Programming Language

マクロの応用として、DSLを作ってみせる内容だが、Racketのモジュールと#langの仕組みを使ってデバッグプリント付きの言語から、Racketに遅延評価を導入してみせたりする。

遅延評価の導入については、Racketで関数適用は、%#appを利用していて、これを差し換えることによって実現する。

この機構のお蔭でevalに手軽にフックを掛けるようなことができ、マクロともリーダーマクロとも違う第三の拡張方法という感じ。

ちなみに、evalにフックを掛ける機構は、Common Lisp系でも*evalhook**applyhook*というものが存在したが、インタプリタ動作とコンパイラ動作の整合性向上のためにANSI Common Lispでは廃止になっている。

結び

Racketが強力というのは良く耳にはするが、あまり触ったことはなかった。
メタプログラミングについては、確かにかなり洗練されていて強力そうだなと感じたので、もうちょっと遊んだりしてみたい。


HTML generated by 3bmd in LispWorks 7.0.0

Lisp本積読解消: Loving Common Lisp — Defining Common Lisp Macros

Posted 2017-03-01 11:48:36 GMT

今回は、Loving Common Lisp, or the Savvy Programmer's Secret Weaponのマクロの章を読む。
2013年に第一版、2016年に第三版が出ている電子書籍。
143頁でKindle版が現在¥583。

最近の書籍だけにQuicklispの説明などもある。

著者のMark Watson氏は、三十余年年のプログラマ人生の中でLispを始め様々な言語を使ってきたが、LispではNLP自然言語処理や、VR仮想現実に取り組んできたとのこと。

1991年にCommon LispでNNニューラルネットワークNLP自然言語処理や、カオス理論を学ぶ本も出したりしている。

但し、1991年という年からも分かるように、時代が一周しているので注意。

Defining Common Lisp Macros

マクロについてはさらっと流していて、説明する内容も、Lispでは構文が拡張できることと、バッククォートの簡単な説明位で、あとは展開形の確認の仕方ということで、macroexpandの説明位。

結び

本書は、最近のCommon Lispについてざっと説明するような内容で、各項目も手頃な分量で書いてある。
最近のCommon Lisp全体を俯瞰するような目的には良いかもしれない。


HTML generated by 3bmd in LispWorks 7.0.0

Lisp本積読解消: 記号処理プログラミング 2.1(b) マクロの機能

Posted 2017-02-26 18:17:43 GMT

今回は、記号処理プログラミングのマクロの箇所を読む。

本書は、岩波講座 ソフトウェア科学の第8巻で、記号処理言語としてLispとPrologの解説があり、記号処理機の説明としてELISの解説もある。
著者の後藤滋樹先生はNUE関係の方でもあるが、本書はTAO/ELISの入門書としても使えたのかもしれない。
出版は、1988年で記述はCommon Lispに合せてある。

マクロの解説は、「親言語としてのLISP」という段落の中にあり、基本的にDSL構築の手法として紹介している。
親言語とは、DSLに対してのホスト言語のこと。

Lispは拡張可能言語であることが紹介され、Fortran等との比較がある。

マクロには、マクロ文字と構文のマクロがあることが解説され、概念的な説明が終わる。

具体的な使い方は別の章で解説となるが、後の章では、ATNを用いた英語の構文解析でDSLを作成するアプローチを紹介し、これにマクロを利用する。
バッククォートやdefmacroの解説もこのDSL作成の過程で解説するようになっている。

基本的にATNのDSLを作成する必要に応じて解説しているので、マクロ機能単体で細かく説明することはないが、こういう説明も面白いと思った。


HTML generated by 3bmd in LispWorks 7.0.0

Lisp本積読解消: Object-Oriented Common Lisp 11 Macros

Posted 2017-02-25 20:18:30 GMT

今回は、Object-Oriented Common Lispのマクロの章を読む。
著者のStephen Slade氏はTの開発者として知られていて、Tについての著作もある。

各章の冒頭は古典からの引用で飾られていて随分知的な雰囲気がある。また語彙が豊富なので英語が苦手な自分のような人間には単語を辞書で確認しつつ読み進めるのが面倒臭い。

まず、Lispでは構文を拡張できることが説明され、repeatという繰り返し構文を作りながら解説が進む。

バッククォートの説明では、defmacroとの組み合わせの他にdeftypeにも言及されていて、これは少し珍しいと思った。

ちなみに、本書でも、Common Lisp Programming for Artificial Intelligenceのバッククォートの説明の箇所が引用されていた。

本書のマクロ作成解説で特徴的なのは、gensymを使わない所。
他の書籍では変数の捕捉問題には大抵gensymを使った対処方法が説明されているが、本書では、変数のスコープを別々にすることで対処する方法のみを説明する。具体的には、repeatのようなものは、

(defmacro repeat (n result &body body)
  `(flet ((body () ,@body)
          (result () ,result))
     (let ((n ,n))
       (do ((count n (1- count)))
           ((<= count 0) (result))
         (body)))))

(repeat 3 nil (print 'a)) ⊳ a ⊳ a ⊳ a → nil

のように本体や結果節を繰り返し構文の変数スコープの外に置くことで捕捉を防ぐ。

ただ上記のようにfletを使った場合、関数名のブロックが作られるため、

(repeat 3 nil 
  (progn (return-from body) (print 'a)))
→ nil

としてしまうと、ブロック名を捕捉できてしまうので、無名関数を使うなり関数名をgensymで生成するなりする必要があるだろう。
なお、本書では上記のような変数以外での捕捉問題の解説はない。

また、局所関数に展開する方法の他に、下請け用関数を定義するバージョンもあり(練習問題の解答で詳細が解説される)

Common Lisp Programming for Artificial Intelligenceでも紹介されていた方法だが、一歩進めた感がある。

次に、関数とマクロの比較、分配束縛、リーダーマクロと解説が進む。
リーダーマクロについては結構詳細な解説がある。

最後に進んだ話題として、リードテーブルの文字ケースの扱い、define-modify-macro、ディスパッチマクロ文字、*macroexpand-hook*について解説がある。

面白いのが、*macroexpand-hook*の解説で、traceのマクロ版を作成しつつ解説が進むが、何故かとてもボリュームがある(8頁も)。
コードが多いとはいえ、*macroexpand-hook*についてここまで詳しい本は他にないのではないだろうか。

練習問題も充実しているが、設問の意図がはっきりしないものが多く、またあまりマクロに関係無い問題もあり飛したくなる。

解答が巻末に付いているので、意味が分からない場合は、解答を見て意図を把握してから問題に取り組むと良いかなと思った。

気になった所

  • ANSI CL規格に沿ってない
  • リードテーブルの扱い
  • 処理系依存なコードが普通に出てくる
  • Common Lispの命名規約に沿ってない

1997年出版であるので、ANS沿って説明かと思いきや、どうもCLtL2相当の説明らしくspecial-form-p等が普通に出てくる。

リードテーブルの扱いというのは、(copy-readtable rt nil)すれば良いのに、無駄なユーティリティを作成しているように見える点。

処理系依存コードについては、Allegro CLに依存しているらしいが、著者はどの辺りが処理系依存かはあまり把握していなさそう。

命名規約に沿わないというのは、スペシャル変数には、*earmuff*を付けるのが規格が採用しているマナー(というか謎のバグ防止)であるのに付けない所。

結び

マクロの章しか読んではいないが、どうも本書は脱線が多いのではないだろうか。
ディスパッチマクロのディスパッチという概念を解説するのに紙テープ時代の話を引用したりとか……。


HTML generated by 3bmd in LispWorks 7.0.0

Lisp本積読解消: はじめてのLisp関数型プログラミング 2.2.8 マクロ関数

Posted 2017-02-24 16:18:21 GMT

今回は、はじめてのLisp関数型プログラミングのマクロの箇所を読む。

本書はLispで関数形関数型プログラミングを学ぶのが主なのでマクロについては3頁程でさらっと流している。

対象のLisp方言は、ISLISPか、Common Lisp。
Lispのマクロは文字レベルではなく、構文レベルであること、バッククォートでテンプレート的にマクロを書く方法、評価方法がざっと書いてある。

3頁に色々詰め込んであるので、初学者にはちょっと難しいかなと思った。
これで興味を持ったら他のもっと詳しく書いてある本で勉強するのが良いと思う。

マクロでifを定義するのに、ifを使う例があったが、そういえば、ifを使わない方法があるのを思い出した。

Henry G. Baker氏のMetacircular Semantics for Common Lisp Special Formsにあるアイデア。
※記憶で書いたらシンボルの属性の使い方が逆だった。

(setf (get 'my-if 'T) 
      (lambda (then else) 
        (declare (ignore else))
        (funcall then)))

(setf (get 'my-if 'NIL) (lambda (then else) (declare (ignore then)) (funcall else)))

(defmacro my-if (pred then &optional else) `(funcall (get 'my-if (not (null ,pred))) (lambda () ,then) (lambda () ,else)))

(defun fib (n) (my-if (< n 2) n (+ (fib (1- n)) (fib (- n 2)))))

(fib 30) → 832040


HTML generated by 3bmd in LispWorks 7.0.0

Lisp本積読解消: ANSI Common Lisp 10 マクロ

Posted 2017-02-21 17:43:33 GMT

今回は、ANSI Common Lisp / ポール・グレアムの10章 マクロを読む。 2003年位に本屋で立ち読みしてLispのことはさっぱり分からないが面白そうなので購入。

10 マクロ

やはり、ポール・グレアム(pg)氏の文章は面白い。
若干の主張の偏りはあるが、面白い文章でCommon Lispの入門ができるというのは素晴しい。
偏っている所が面白い所でもある。

まず、評価器の話から始めて、マクロの話につなげる。
マクロのメリットが説明されるが、ここで脚注に関数の評価について何故か細かい注釈がある。
関数と引数の評価についてだが、ANSでいうと、

あたりの話だと思われる

(progn
  (defun foo (x) (+ x 3))
  (defun bar () (setf (fdefinition 'foo) (lambda (x) (+ x 4))))
  (foo (progn (bar) 20)))

上記が23になるか、24になるかは処理系依存。
ちなみに、SBCL、GCL、Allegro CLあたりは23、LispWorks、Clozure CL、ECLは24になった。同じKCL系のGCLとECLで違っているのがちょっと不思議

更に話が逸れるが、Macro Forms as Placesの話もさらっと書いてあった。
読んだ筈だったが、すっかり忘却してしまっていたらしい……。

閑話休題。次にバッククォートの説明がされ、マクロの使い方と、マクロの設計について、またマクロ作成時に注意する点等が一通り解説される。

マクロの設計について色々語る本は少ないと思う(On LispとかLet Over Lambda等目立つ本はあるが……)。

次に、一般化参照の解説と、マクロを書くためのユーティリティを解説し、最後に、未だ開拓され尽されてはいないマクロの可能性で締める。

練習問題も考えさせるような問題で良くできていると思った。

気になった所

aifitcall-next-methodの引数省略のようなものが同列に説明されているけれど、ちょっと違うのではないかなあと思った。

結び

「マクロを書くというのは〜コンパイラを書き換えられるのとほとんど一緒である」のような言い回しにやはりセンスを感じる。

解説内容も親切で詳しいし、この章にCommon Lispのマクロを書く上で必要なことは、殆ど全部書いてある。面白いので読んだことがない人にはお勧めしたい。
また、pg氏のようにマクロのユーティリティを積み上げていく独特のスタイルについては、On Lispのコードや、ユーティリティ集、Arcのユーティリティのソースコードを読むと雰囲気がつかめる


HTML generated by 3bmd in LispWorks 7.0.0

Older entries (2090 remaining)