現在第4章の超循環評価器を作っています。
4章の一番最初に出てくるevalの定義は以下の通り
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(define (eval exp env) | |
(cond ((self-evaluating? exp) exp) | |
((variable? exp) (lookup-variable-value exp env)) | |
((quoted? exp) (text-of-quotation exp)) | |
((assignment? exp) (eval-assignment exp env)) | |
((definition? exp) (eval-definition exp env)) | |
((if? exp) (eval-if exp env)) | |
((lambda? exp) | |
(make-procedure (lambda-parameters exp) | |
(lambda-body exp) | |
env)) | |
((begin? exp) | |
(eval-sequence (begin-actions exp) env)) | |
((cond? exp) (eval (cond->if exp) env)) | |
((application? exp) | |
(apply (eval (operator exp) env) | |
(list-of-values (operands exp) env))) | |
(else | |
(error "Unknown expression type -- EVAL" exp)))) |
これをClojureっぽく書くことを考えてみる。
Protocol
ClojureといったらやっぱProtocolだよね!ということでプロトコルベースで考える。
とりあえず評価器プロトコル、Eval(uator)の定義
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defprotocol Eval | |
(eval [this env exp])) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(def nothing (Object.)) | |
(defn literal? [exp] | |
(or (true? exp) | |
(false? exp) | |
(nil? exp) | |
(number? exp) | |
(string? exp))) | |
(def literal | |
(reify Eval | |
(eval [this env exp] | |
(if (literal? exp) | |
exp | |
nothing)))) |
今回はとりあえずこれで。
このように、小さな評価器をいっぱい作って合成していくという考え方。
special form
ifやdefineやbeginなんかのspecial formをチェックする関数tagged?の定義
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defn tagged? [tag exp] | |
(and (seq? exp) | |
(-> exp empty? not) | |
(-> exp first (= tag)))) |
これを使って、special formを以下のように表してみる
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(deftype Special [tag f] | |
Eval | |
(eval [this env exp] | |
(if (tagged? tag exp) | |
(f env (rest exp)) | |
nothing))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(def quotation | |
(Special. 'quote | |
(fn [env exp] | |
(first exp)))) |
if
ifは式を評価しなければいけない。
しかし、どの評価器を使えばいいのかわからない。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(def divergence | |
(Special. 'if | |
(fn [env exp] | |
(if (->> exp first (eval ? env)) | |
(eval ? env (nth exp 1)) | |
(eval ? env (nth exp 2)))))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(def lisp (compose literal | |
quotation | |
(divergence lisp))) |
ここでは評価機が部分適用されたeval関数をとることにする。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defn divergence [f] | |
(Special. 'if | |
(fn [env exp] | |
(if (->> exp first (f env)) | |
(f env (nth exp 1)) | |
(f env (nth exp 2)))))) |
compose
一番大事な評価器を合成する関数
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defn compose [e & es] | |
(reduce (fn [e e'] | |
(reify Eval | |
(eval [this env exp] | |
(let [result (eval e env exp)] | |
(if (= result nothing) | |
(eval e' env exp) | |
result))))) | |
e | |
es)) |
これを使ってifとliteralだけが使えるlispを定義すると以下のようになる。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(def lisp | |
(compose literal | |
(divergence (fn [env exp] | |
(eval lisp env exp))))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
user=> (eval lisp {} '(if true 1 2)) | |
1 | |
user=> (eval lisp {} '(if false 1 2)) | |
2 |
まとめ
こんな感じで小さな評価器を書いていって大きな評価器を作っています。
なかなか楽しい。
今回のプログラムはScalaBaseで@maeda_さんが発表されていた、Javascript as an Embedded DSLに触発されて書いてみました。
おもしろいので読んでみるとよいです。
次はぱーさーこんびねーたについて書く、書きたい、書けたらいいな。
0 件のコメント:
コメントを投稿