ダグ・ホイト : Let Over Lambda

作成日:2020-10-30
最終更新日:

概要

Lisp におけるマクロについての本。

感想

実は買った本ではない。図書館で借りた本だ。 難しい。

p.145 の本文に、《自由変数注入を理解する難しさの原因について、一番もっともらしい仮説は、 そのフェイルセイフな挙動にあるというものである††。》という主張がある。 この脚注††の言葉が印象深い。
《ここでの安全(セイフ)の意味は実世界のそれとは真逆であり、 可能な限り速やかに失敗して声高にそれを知らせることが最も安全とされる。》

誤植

p.143 上から 10 行目、《先ほど Alet Over AlambdaAlet Over Alambda カウンタ》とあるが、 明らかに重複している。

そのほか、助詞が足りないレベルの誤植がある。

p.207 上から3行目、《充分な型情報あれば》→《充分な型情報があれば》

p.221 本文上から3行目、《プールを使い切らないこと前提にできる》→《プールを使い切らないことを前提にできる》

p.229 上から1行目、《ビット演算おいて》→《ビットに演算おいて》

SBCL を使うとき

Steel Bank Common Lisp (SBCL) を使うときは、注意しなければならない。 https://letoverlambda.com/index.cl/errata に書いてある通りである。

If you are using SBCL, you must use the production code since it contains some important fixes related to how SBCL handles quasiquotes.

なぜこれに気づいたかというと、 第7章の Forth が気になって読み進めていったからだ。 バッククォート(疑似クォート)を含む p.265 のプログラムのマクロ定義 forth-install-prims がエラーになった。

さて、この Forth の個所がどのように直されているのか、気になって github を見たが、 どういうわけか Forth に関する修正部分がない。これを自分でやれというのはきつい。 でも、やらないといけないのかな。

どうやらライブラリとしてインストールできるようだ。roswell を入れているので、 うまくできるかもしれない。

$ ros install let-over-lambda
To load "let-over-lambda":
  Load 3 ASDF systems:
	alexandria cl-ppcre named-readtables
  Install 1 Quicklisp release:
	let-over-lambda
Downloading http://beta.quicklisp.org/archive/let-over-lambda/2015-09-23/let-over-lambda-20150923-git.tgz
##########################################################################
; Loading "let-over-lambda"
[package let-over-lambda].......
[1/3] System 'let-over-lambda' found. Loading the system..
[2/3] Processing build-hook..
[3/3] Attempting to install the scripts in roswell/ subdirectory of the system...
No roswell scripts found.

もう一度やってみた。

$ ros install let-over-lambda
To load "let-over-lambda":
  Load 1 ASDF system:
    let-over-lambda
; Loading "let-over-lambda"

; compiling file "/home/username/.roswell/lisp/quicklisp/dists/quicklisp/software/let-over-lambda-20150923-git/package.lisp" (written 28 AUG 2015 11:51:46 PM):
; processing (DEFPACKAGE #:LET-OVER-LAMBDA ...)

; wrote /home/username/.cache/common-lisp/sbcl-2.0.10-linux-x64/home/username/.roswell/lisp/quicklisp/dists/quicklisp/software/let-over-lambda-20150923-git/package-tmpAAURSO1.fasl
; compilation finished in 0:00:00.012
; compiling file "/home/username/.roswell/lisp/quicklisp/dists/quicklisp/software/let-over-lambda-20150923-git/let-over-lambda.lisp" (written 28 AUG 2015 11:51:46 PM):
; processing (IN-PACKAGE #:LET-OVER-LAMBDA)
; processing (DEFUN GROUP ...)
; processing (DEFUN MKSTR ...)
; processing (DEFUN SYMB ...)
; processing (DEFUN FLATTEN ...)
; processing (DEFUN G!-SYMBOL-P ...)
; processing (DEFUN O!-SYMBOL-P ...)
; processing (DEFUN O!-SYMBOL-TO-G!-SYMBOL ...)
; processing (DEFMACRO DEFMACRO/G! ...)
; processing (DEFMACRO DEFMACRO! ...)
; processing (DEFMACRO DEFUN! ...)
; processing (DEFUN |#"-reader| ...)
; processing (DEFUN |#>-reader| ...)
; processing (DEFUN SEGMENT-READER ...)
; processing (DEFMACRO! MATCH-MODE-PPCRE-LAMBDA-FORM ...)
; processing (DEFMACRO! SUBST-MODE-PPCRE-LAMBDA-FORM ...)
; processing (DEFUN |#~-reader| ...)
; processing (DEFMACRO! DLAMBDA ...)
; processing (DEFMACRO ALAMBDA ...)
; processing (DEFMACRO AIF ...)
; processing (DEFUN |#`-reader| ...)
; processing (DEFUN |#f-reader| ...)
; processing (DEFREADTABLE LOL-SYNTAX ...)[1/3] System 'let-over-lambda' found. Loading the system..

; file: /home/username/.roswell/lisp/quicklisp/dists/quicklisp/software/let-over-lambda-20150923-git/let-over-lambda.lisp
; in: DEFREADTABLE LOL-SYNTAX
;     (EDITOR-HINTS.NAMED-READTABLES:DEFREADTABLE LET-OVER-LAMBDA:LOL-SYNTAX
;       (:MERGE :STANDARD)
;       (:DISPATCH-MACRO-CHAR #\# #\" #'LET-OVER-LAMBDA:|#"-reader|)
;       (:DISPATCH-MACRO-CHAR #\# #\> #'LET-OVER-LAMBDA::|#>-reader|)
;       (:DISPATCH-MACRO-CHAR #\# #\~ #'LET-OVER-LAMBDA:|#~-reader|)
;       (:DISPATCH-MACRO-CHAR #\# #\` #'LET-OVER-LAMBDA:|#`-reader|)
;       (:DISPATCH-MACRO-CHAR #\# #\f #'LET-OVER-LAMBDA:|#f-reader|))
; --> EVAL-WHEN
; ==>
;   (LET ((READTABLE
;          (EDITOR-HINTS.NAMED-READTABLES:FIND-READTABLE
;           'LET-OVER-LAMBDA:LOL-SYNTAX)))
;     (COND ((NOT READTABLE) (SETQ READTABLE #))
;           (T (SETQ READTABLE #)
;            (EDITOR-HINTS.NAMED-READTABLES::SIMPLE-STYLE-WARN
;             "Overwriting already existing readtable ~S." READTABLE)))
;     (EDITOR-HINTS.NAMED-READTABLES:MERGE-READTABLES-INTO READTABLE ':STANDARD)
;     (SET-DISPATCH-MACRO-CHARACTER #\# #\" #'LET-OVER-LAMBDA:|#"-reader|
;                                   READTABLE)
;     (SET-DISPATCH-MACRO-CHARACTER #\# #\> #'LET-OVER-LAMBDA::|#>-reader|
;                                   READTABLE)
;     (SET-DISPATCH-MACRO-CHARACTER #\# #\~ #'LET-OVER-LAMBDA:|#~-reader|
;                                   READTABLE)
;     (SET-DISPATCH-MACRO-CHARACTER #\# #\` #'LET-OVER-LAMBDA:|#`-reader|
;                                   READTABLE)
;     (SET-DISPATCH-MACRO-CHARACTER #\# #\f #'LET-OVER-LAMBDA:|#f-reader|
;                                   READTABLE)
;     READTABLE)
;
; caught STYLE-WARNING:
;   Overwriting already existing readtable #<NAMED-READTABLE LOL-SYNTAX {100506FE23}>.

; processing (IN-READTABLE LOL-SYNTAX)
; processing (DEFMACRO! NLET-TAIL ...)
; processing (DEFMACRO ALET% ...)
; processing (DEFMACRO ALET ...)
; processing (DEFUN LET-BINDING-TRANSFORM ...)
; processing (DEFMACRO PANDORICLET ...)
; processing (DEFUN PANDORICLET-GET ...)
; processing (DEFUN PANDORICLET-SET ...)
; processing (DECLAIM (INLINE GET-PANDORIC))
; processing (DEFUN GET-PANDORIC ...)
; processing (DEFSETF GET-PANDORIC ...)
; processing (DEFMACRO WITH-PANDORIC ...)
; processing (DEFUN PANDORIC-HOTPATCH ...)
; processing (DEFMACRO PANDORIC-RECODE ...)
; processing (DEFMACRO PLAMBDA ...)
; processing (DEFVAR PANDORIC-EVAL-TUNNEL)
; processing (DEFMACRO PANDORIC-EVAL ...)
; processing (DEFMACRO FAST-PROGN ...)
; processing (DEFMACRO SAFE-PROGN ...)
; processing (DEFUN FFORMAT ...)
; processing (DEFINE-COMPILER-MACRO FFORMAT ...)
; processing (DECLAIM (INLINE MAKE-TLIST ...))
; processing (DEFUN MAKE-TLIST ...)
; processing (DEFUN TLIST-LEFT ...)
; processing (DEFUN TLIST-RIGHT ...)
; processing (DEFUN TLIST-EMPTY-P ...)
; processing (DECLAIM (INLINE TLIST-ADD-LEFT ...))
; processing (DEFUN TLIST-ADD-LEFT ...)
; processing (DEFUN TLIST-ADD-RIGHT ...)
; processing (DECLAIM (INLINE TLIST-REM-LEFT))
; processing (DEFUN TLIST-REM-LEFT ...)
; processing (DECLAIM (INLINE TLIST-UPDATE))
; processing (DEFUN TLIST-UPDATE ...)
; processing (DEFUN BUILD-BATCHER-SN ...)
; processing (DEFMACRO! SORTF ...)
; processing (DEFUN DOLLAR-SYMBOL-P ...)
; processing (DEFMACRO! IF-MATCH ...)
; processing (DEFMACRO WHEN-MATCH ...)

; wrote /home/username/.cache/common-lisp/sbcl-2.0.10-linux-x64/home/username/.roswell/lisp/quicklisp/dists/quicklisp/softwa
re/let-over-lambda-20150923-git/let-over-lambda-tmp5GEXGEG5.fasl
; compilation finished in 0:00:00.463
WARNING:
   Lisp compilation had style-warnings while
   compiling #<CL-SOURCE-FILE "let-over-lambda" "let-over-lambda">
;
; compilation unit finished
;   caught 1 STYLE-WARNING condition
[2/3] Processing build-hook..
[3/3] Attempting to install the scripts in roswell/ subdirectory of the system...
No roswell scripts found.

$ ros run で以下実施してみた

* (lol:flatten '((A . B) (C . D) (E . (F G H (I . J) . K))))

debugger invoked on a SB-INT:SIMPLE-READER-PACKAGE-ERROR in thread
(中略)
abort

ということは、ここで leg-over-lambda を読み込まないといけないのだろう。

* (ql:quickload :let-over-lambda)
To load "let-over-lambda":
  Load 1 ASDF system:
    let-over-lambda
; Loading "let-over-lambda"
..
(:LET-OVER-LAMBDA)
*  (named-readtables:in-readtable lol:lol-syntax)
#<NAMED-READTABLE LET-OVER-LAMBDA:LOL-SYNTAX {100184A243}>
* (lol:flatten '((A . B) (C . D) (E . (F G H (I . J) . K))))
(A B C D E F G H I J K)

これで一歩進んだかもしれない。Forth の例が実行できるか、確かめる。

* (load "forth-registers.lisp") ; リスト7.1
T
* (load "forth-word.lisp") ; リスト7.2
T
* (load "forth-lookup.lisp") ; リスト7.3
T
* (load "forth-inner-interpreter.lisp") ; リスト7.4
T
* (load "prim-forms.lisp") ; リスト7.5
T
* (load "basic-prim-forms.lisp") ; リスト7.6
T
* (load "go-forth.lisp") ; リスト7.7
(中略)
;
; compilation unit finished
;   Undefined functions:
;     DEFMACRO! O!FORTH
;   Undefined variables:
;     &REST G!FORTH GO-FORTH WORDS
;   caught 4 WARNING conditions
;   caught 2 STYLE-WARNING conditions

ここで、defmacro! 関数がない、o!forth 関数がないことがわかった。 また、&rest、g!forth、go-forth、words のそれぞれの変数がないことがわかった。

そこで、リスト 7.7 の defmacro! を lol:defmacro! に修正してみると大丈夫だ。

* (load "go-forth.lisp") ; リスト7.7
T
* (load "forth-stdlib.lisp") ; リスト7.8
T
* (load "new-forth.lisp"); リスト7.9 
T

リスト 7.9 では、alet や plambda など、本書で定義された関数やマクロがある。 しかし、7.9 はマクロの定義だから、Undefined functions などは検出されない。 あえて何もせずに、次にいってしまおう。

* (load "forth-install-prims.lisp") ; リスト7.10
T
* (load "forth-prims-compilation-control.lisp") ; リスト 7.11
(中略)	
debugger invoked on a TYPE-ERROR in thread
#<THREAD "main thread" RUNNING {1004A90103}>:
	  The value
		[
	  is not of type
		LIST
	  when binding LIST	

悪運が尽きたようだ。リスト 7.11 は def-forth-prim によって Forth のプリミティブを定義しているのだが、 この def-forth-prim が正しくないようだ。しかし、def-forth-prim はリスト 7.5 でマクロとして定義されている。 この定義では def-forth-naked-prim を呼び出している。 def-forth-naked-prim はやはりマクロで定義され、 この定義には特に問題はないように見える。どこがおかしかったのか。放置して先に進む。

* (load "forth-compile-in.lisp") ; リスト 7.12
T
* (load "forth-handle-found.lisp") ; リスト 7.13
T
* (load "forth-handle-not-found.lisp") リスト 7.14
T

では、p.269 で示されたように、 new-forth マクロを使って新しいインスタンスを作れるかどうか。

* (setq my-forth (new-forth))
(中略)
; compilation unit finished
;   Undefined functions:
;     ALET AN FORTH-COMPILI-IN FORTH-WORD-THRAED PLAMBDA PSTACK V
;   Undefined variables:
;     COMPILING DICT DTABLE MY-FORTH PC PSTACK RSTACK THIS V
;   caught 9 WARNING conditions
;   caught 7 STYLE-WARNING conditions

リスト 7.9 の alet を lol:alet に、plambda を lol:plambda に書き換えた。 もう一度やってみたらどうだろう。

* (setq my-forth (new-forth))
(中略)
; compilation unit finished
;   Undefined functions:
;     AN FORTH-COMPILI-IN FORTH-WORD-THRAED
;   Undefined variables:
;     MY-FORTH THIS
;   caught 2 WARNING conditions
;   caught 3 STYLE-WARNING conditions

未定義関数はタイプミスのようだ。これらのタイプミスを直して再度試す。

* (setq my-forth (new-forth))
; in: SETQ MY-FORTH
;     (SETQ MY-FORTH (NEW-FORTH))
;
; caught WARNING:
;   undefined variable: COMMON-LISP-USER::MY-FORTH

;     (NEW-FORTH)
; --> LET-OVER-LAMBDA:ALET LET DOLIST BLOCK LET TAGBODY UNLESS IF PROGN LET
; --> TAGBODY FUNCALL SB-C::%FUNCALL THE
; ==>
;   (SB-KERNEL:%COERCE-CALLABLE-FOR-CALL THIS)
;
; caught WARNING:
;   undefined variable: COMMON-LISP-USER::THIS
;
; compilation unit finished
;   Undefined variables:
;     MY-FORTH THIS
;   caught 2 WARNING conditions
#<CLOSURE (LAMBDA (&REST LET-OVER-LAMBDA::PARAMS)) {100373AD6B}>

MY-FORTH と THIS が未定義変数だといっている。p.269 の脚注を参照すると、 MY-FORTH のほうは、setq の代わりに defparameter を使うことで回避できそうだ。THIS のほうはどういうことだろう。

* (defparameter my-forth (new-forth))
; in: DEFPARAMETER MY-FORTH
;     (FUNCALL THIS V)
; --> SB-C::%FUNCALL THE
; ==>
;   (SB-KERNEL:%COERCE-CALLABLE-FOR-CALL THIS)
;
; caught WARNING:
;   undefined variable: COMMON-LISP-USER::THIS
;
; compilation unit finished
;   Undefined variable:
;     THIS
;   caught 1 WARNING condition
MY-FORTH

そういえば、この THIS はリスト 7.9 の alet で出てきたのだった。 ということは、リスト 7.9 では生の this は使えず、lml:this を使わないといけないはずだ。

書き換えてみると、次のプログラムは実行できる

* (defparameter my-forth (new-forth))
MY-FORTH
* (go-forth my-forth
	2 3 * print)

6
NIL

次からはうまくいかない

* (def-forth-prim [ t  (setf compiling nil))
(([ T (SETF COMPILING NIL) (SETF PC (CDR PC)))
 (IMMEDIATE NIL (SETF (FORTH-WORD-IMMEDIATE DICT) T) (SETF PC (CDR PC)))
 (NAME NIL (SETF (FORTH-WORD-NAME DICT) (POP PSTACK)) (SETF PC (CDR PC)))
 (CREATE NIL (SETF DICT (MAKE-FORTH-WORD :PREV DICT)) (SETF PC (CDR PC)))
 (R> NIL (PUSH (POP RSTACK) PSTACK) (SETF PC (CDR PC)))
 (>R NIL (PUSH (POP PSTACK) RSTACK) (SETF PC (CDR PC)))
 (PRINT NIL (PRINT (POP PSTACK)) (SETF PC (CDR PC)))
 (SWAP NIL (ROTATEF (CAR PSTACK) (CADR PSTACK)) (SETF PC (CDR PC)))
 (DUP NIL (PUSH (CAR PSTACK) PSTACK) (SETF PC (CDR PC)))
 (DROP NIL (POP PSTACK) (SETF PC (CDR PC)))
 (* NIL (PUSH (* (POP PSTACK) (POP PSTACK)) PSTACK) (SETF PC (CDR PC)))
 (NOP NIL (SETF PC (CDR PC))))
* (def-forth-prim ] nil  (setf compiling t))
((] NIL (SETF COMPILING T) (SETF PC (CDR PC)))
 ([ T (SETF COMPILING NIL) (SETF PC (CDR PC)))
 (IMMEDIATE NIL (SETF (FORTH-WORD-IMMEDIATE DICT) T) (SETF PC (CDR PC)))
 (NAME NIL (SETF (FORTH-WORD-NAME DICT) (POP PSTACK)) (SETF PC (CDR PC)))
 (CREATE NIL (SETF DICT (MAKE-FORTH-WORD :PREV DICT)) (SETF PC (CDR PC)))
 (R> NIL (PUSH (POP RSTACK) PSTACK) (SETF PC (CDR PC)))
 (>R NIL (PUSH (POP PSTACK) RSTACK) (SETF PC (CDR PC)))
 (PRINT NIL (PRINT (POP PSTACK)) (SETF PC (CDR PC)))
 (SWAP NIL (ROTATEF (CAR PSTACK) (CADR PSTACK)) (SETF PC (CDR PC)))
 (DUP NIL (PUSH (CAR PSTACK) PSTACK) (SETF PC (CDR PC)))
 (DROP NIL (POP PSTACK) (SETF PC (CDR PC)))
 (* NIL (PUSH (* (POP PSTACK) (POP PSTACK)) PSTACK) (SETF PC (CDR PC)))
 (NOP NIL (SETF PC (CDR PC))))
* (go-forth my-forth create)
NIL
* (go-forth my-forth  ] dup * [)

debugger invoked on a SIMPLE-ERROR in thread
#<THREAD "main thread" RUNNING {1004A90103}>:
  Word ] not found

Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
  0: [ABORT] Exit debugger, returning to top level.

((LAMBDA (V)) ])
   source: (FORTH-HANDLE-FOUND)
0] abort
* 

これ以降もあがいてみたが、どうにも進まなかった。悔しいが、あきらめる。

書誌情報

書 名Let Over Lambda
著 者ダグ・ホイト
発行日 年 月 日
発行所エスアイビー・アクセス
定 価 円(税別)
サイズ
ISBN
NDC

まりんきょ学問所関数型言語 LISP > ダグ・ホイト:Let Over Lambda


MARUYAMA Satosi