iアプリでScheme(Lisp)処理系を作る 第三回

今回はコードはありません。
ifなどの「構文」を追加しようとしていろいろ調べ物をしていたら考えが収束しなくなってしまいました。
まとまったコードは次回に掲載する予定です。
それとPairのcdrをいちいちキャストするのが面倒で精神衛生上良くないのもなんとかしたいところ。

今回は実装上迷ってしまった部分をメモとして書き残すことにします。
lispの式は
(<手続き> <引数> ...)
例:(+ 1 2)
で手続き(=いわゆる関数)を行えるのは少し触ってみればわかるのですが、ここで手続きというものを少し整理してみたいと思います。

手続き(Procedure)

  • 組み込み手続き Function/Subr 引数を不定順に評価して渡す。環境は不要
  • 特殊形式手続き SpecialForm 引数を評価せずに渡す。呼び出し元の環境にて評価
  • 自作手続き Closure(λ式が作成) 不定順に評価。λ式適用時の環境を保存
  • Lispマクロ Macro
  • 継続 Continuation

だいたいこんな感じだろうと思っていました。
なにせ全部表記が一緒なので。

そこが落とし穴だったようです。

Gauche(ゴーシュ)の対話式入力窓をひらいて、以下の式を評価してみます。

  1. (+ 1 2)
  2. ((lambda (x) (+ 1 x) ) 2)
  3. (if #t (+ 1 2))

当然みんな3を返すのを確認しました。
さて、ここで先頭部の"手続き"を(or 〜)で包んでみます。
orは偽が返されるまで左から引数を次々に評価し最後に評価された値を返します。一引数ならそれを評価して返すということです。

  1. ((or +) 1 2)
  2. ((or (lambda (x) (+ 1 x))) 2)
  3. ((or if) #t (+ 1 2) )

(or +)は+、(or (lambda x x))は#、(or if)は#になるようなのでこれもまた全部3を返すはずです。
ところが実際は・・・

gosh> ((or +) 1 2)
3
gosh> ((or (lambda (x) (+ 1 x))) 2)
3
gosh> ((or if) #t (+ 1 2)))
*** ERROR: invalid application: (#<syntax if> #t 3)
Stack Trace:
_______________________________________

3.だけエラーになってしまいました。

もういちど仕様書を見直すと次のようにあります

4.1.3. 手続き呼出し

(<演算子> <オペランド1> ...) 構文

手続き呼出しは,呼び出される手続きとしての式と,
その手続きに渡される引数としての各式を,
単純に丸カッコでくくることによって書かれる。
演算子オペランドの各式が(未規定の順序で) 評価され,そしてその結果として得られた手続きに,
結果として得られた引数が渡される。

特殊形式=構文、つまり構文も括弧の内側にあるものだとおもっていたのでこれには面食らいました。同じ表記なのですべて同格だと勘違いしていました。
Common Lispの実装ならそれでもいいのかもしれませんが、
これはSchemeです。Schemeなんです。←これ重要!そういえば特殊形式(スペシャルフォーム)自体CLの用語だったような気がします。

・・・

仕様書を再度読み込んで上のまとめを修正したものが以下です。

構文(Syntax)

  • (if 〜 〜 〜)
    • 第一引数が真なら第二を、偽なら第三引数を評価して返す
  • (quote 〜)
    • 第一引数を評価せずに返す
  • (<演算子> 〜〜〜)
    • <演算子>は評価すると手続き(Procedure)になる。すべての式が未規定の順序で評価され、得られた手続きに引数を与えて評価させる
      • 組み込み手続き Function/Subr
        • (call-with-current-continuation 〜) [継続]
      • 新しい手続き Closure(λ式が作成) λ式使用時の環境を保存
  • (let-syntax 〜 〜) [衛生的なマクロ]
    • オリジナルの構文をガンガン増やせる。マクロの展開によって新構文は置き換えされる。