Upload
sohta
View
2.119
Download
4
Embed Size (px)
Citation preview
Macros in Clojure@athos0220
• Lispマクロ入門
• Clojureのマクロとその周辺
• Clojureマクロの応用例
Agenda
Lispマクロ入門
マクロとは
構文木を組み替えるための仕組み→自由に構文を作ることができる仕組み
if
=
print print
"Fizz" "Buzz"
mod 0
x 15
do nil
when
= print print
"Fizz" "Buzz"mod 0
x 15
(when (= (mod x 15) 0) (print “Fizz”) (print “Buzz”))
(if (= (mod x 15) 0) (do (print “Fizz”) (print “Buzz”)) nil)
マクロの定義• シンボルやリストで展開形のコードを作ってやる
(when ◯◯ △△ ✕✕)
(if ◯◯ (do △△ ✕✕) nil)
マクロの定義• シンボルやリストで展開形のコードを作ってやる
(when ◯◯ △△ ✕✕)
(if ◯◯ (do △△ ✕✕) nil)
(defmacro when [test & body] (list ’if test (cons ’do body) nil))
マクロの定義• シンボルやリストで展開形のコードを作ってやる
(when ◯◯ △△ ✕✕)
(if ◯◯ (do △△ ✕✕) nil)
(defmacro when [test & body] (list ’if test (cons ’do body) nil))
(defmacro when [test & body] `(if ~test (do ~@body) nil)
名前衝突の問題
名前衝突の問題束縛変数の衝突 自由変数の衝突
名前衝突の問題
(defmacro or [expr1 expr2] `(let [val ~expr1] (if val val ~expr2)))
束縛変数の衝突 自由変数の衝突
名前衝突の問題
(defmacro or [expr1 expr2] `(let [val ~expr1] (if val val ~expr2)))
(let [val true] (or false val))
束縛変数の衝突 自由変数の衝突
(let [val true] (let [val false] (if val val val))
名前衝突の問題
(defmacro or [expr1 expr2] `(let [val ~expr1] (if val val ~expr2)))
(let [val true] (or false val))
束縛変数の衝突 自由変数の衝突
名前衝突の問題
(defmacro or [expr1 expr2] `(let [val ~expr1] (if val val ~expr2)))
(let [val true] (or false val))
(let [val true] (let [val false] (if val val val))
束縛変数の衝突 自由変数の衝突
名前衝突の問題
(defmacro or [expr1 expr2] `(let [val ~expr1] (if val val ~expr2)))
(let [val true] (or false val))
(let [val true] (let [val false] (if val val val))
束縛変数の衝突 自由変数の衝突
名前衝突の問題
(defmacro or [expr1 expr2] `(let [val ~expr1] (if val val ~expr2)))
(let [val true] (or false val))
(let [val true] (let [val false] (if val val val))
(let [let nil] (or false true))
束縛変数の衝突 自由変数の衝突
(let [let nil] (let [val false] (if val val true))
名前衝突の問題
(defmacro or [expr1 expr2] `(let [val ~expr1] (if val val ~expr2)))
(let [val true] (or false val))
(let [val true] (let [val false] (if val val val))
(let [let nil] (or false true))
束縛変数の衝突 自由変数の衝突
名前衝突の問題
(defmacro or [expr1 expr2] `(let [val ~expr1] (if val val ~expr2)))
(let [val true] (or false val))
(let [val true] (let [val false] (if val val val))
(let [let nil] (or false true))
(let [let nil] (let [val false] (if val val true))
束縛変数の衝突 自由変数の衝突
名前衝突の問題
(defmacro or [expr1 expr2] `(let [val ~expr1] (if val val ~expr2)))
(let [val true] (or false val))
(let [val true] (let [val false] (if val val val))
(let [let nil] (or false true))
(let [let nil] (let [val false] (if val val true))
束縛変数の衝突 自由変数の衝突
健全なマクロ (Hygienic macros)
• 名前の衝突を自動的に回避してくれるハイパーなマクロ
健全なマクロ (Hygienic macros)
• 名前の衝突を自動的に回避してくれるハイパーなマクロ
(define-syntax or (syntax-rules () ((or expr1 expr2) (let ([val expr1]) (if val val expr2)))))
健全なマクロ (Hygienic macros)
• 名前の衝突を自動的に回避してくれるハイパーなマクロ
(define-syntax or (syntax-rules () ((or expr1 expr2) (let ([val expr1]) (if val val expr2)))))
(let ([val true]) (or false val))
健全なマクロ (Hygienic macros)
• 名前の衝突を自動的に回避してくれるハイパーなマクロ
(define-syntax or (syntax-rules () ((or expr1 expr2) (let ([val expr1]) (if val val expr2)))))
(let ([val true]) (or false val))
(let ([val true]) (let ([val_0 false]) (if val_0 val_0 val)))
マクロの分類
低レベル
不健全
高レベル
健全
• 伝統的なマクロ
• syntactic closures• explicit renaming• syntax-case
• syntax-rules
• 高レベル:専用のパターン言語をもつ• 低レベル:Lispの関数でコードを操作する
マクロの分類
低レベル
不健全
高レベル
健全
• 伝統的なマクロ
• syntactic closures• explicit renaming• syntax-case
• syntax-rules
>
• 高レベル:専用のパターン言語をもつ• 低レベル:Lispの関数でコードを操作する
マクロの分類
低レベル
不健全
高レベル
健全
• 伝統的なマクロ
• syntactic closures• explicit renaming• syntax-case
• syntax-rules
>>
• 高レベル:専用のパターン言語をもつ• 低レベル:Lispの関数でコードを操作する
Clojureのマクロシステムとその周辺
Clojureのマクロシステム
• 伝統的なマクロがベース
• syntax-quote が特徴
低レベル
不健全
高レベル
健全
• 伝統的なマクロ
• syntactic closures• explicit renaming• syntax-case
• syntax-rules
>>
syntax-quote
syntax-quote束縛変数の衝突 自由変数の衝突
(defmacro or [expr1 expr2] `(let [val# ~expr1] (if val# val# ~expr2)))
syntax-quote束縛変数の衝突 自由変数の衝突
syntax-quote束縛変数の衝突 自由変数の衝突
(defmacro or [expr1 expr2] `(let [val# ~expr1] (if val# val# ~expr2)))
syntax-quote
syntax-quote束縛変数の衝突 自由変数の衝突
(defmacro or [expr1 expr2] `(let [val# ~expr1] (if val# val# ~expr2)))
(let [val true] (or false val))
syntax-quote
syntax-quote束縛変数の衝突 自由変数の衝突
(defmacro or [expr1 expr2] `(let [val# ~expr1] (if val# val# ~expr2)))
(let [val true] (or false val))
(let [val true] (clojure.core/let [val__419__auto__ false] (if val__419__auto__ val__419__auto__ val))
syntax-quote
#をつけた名前を自動でリネーム (auto-gensym)
syntax-quote束縛変数の衝突 自由変数の衝突
(defmacro or [expr1 expr2] `(let [val# ~expr1] (if val# val# ~expr2)))
(let [val true] (or false val))
(let [val true] (clojure.core/let [val__419__auto__ false] (if val__419__auto__ val__419__auto__ val))
(let [let nil] (or false true))
syntax-quote
#をつけた名前を自動でリネーム (auto-gensym)
syntax-quote束縛変数の衝突 自由変数の衝突
(defmacro or [expr1 expr2] `(let [val# ~expr1] (if val# val# ~expr2)))
(let [let nil] (clojure.core/let [val__419__auto__ false] (if val__419__auto__ val__419__auto__ true))
(let [val true] (or false val))
(let [val true] (clojure.core/let [val__419__auto__ false] (if val__419__auto__ val__419__auto__ val))
(let [let nil] (or false true))
syntax-quote
#をつけた名前を自動でリネーム (auto-gensym) その他の名前にはnamespace名をqualifyする
メタデータ• ほとんどすべてのオブジェクトにメタデータを付加できる
• Clojureではコードもデータ
→コードのほとんどあらゆる部分にメタデータをアノテーションとして付加できる
(defn ^:private f [x] x)
(def ^:dynamic x nil)
(defn fact ^long [^long x] (if (= x 0) 1 (* x (fact (- x 1)))))
名前をnamespaceにプライベートにする
動的スコープの変数にする 戻り値と引数の型ヒント
Java Interop
• ClojureからJavaのクラスにアクセスできる
• Clojureは大部分がJavaで書かれている
• Javaで書かれているClojureのコンパイラ自体や内部で使われる構文木にも触れる
暗黙の引数 &formと&env
• マクロ呼出しのフォームを囲むコンテキストに関する情報が渡ってくる
- &form:マクロ呼出しのフォーム全体
- &env:マクロ呼出しの時点で見えているローカル環境
Clojure
マクロの応用例
the オペレータhttp://d.hatena.ne.jp/athos/20120129/THE_operator_in_clojure
• 「型を1つ引数にとり現在のスコープに唯一存在するその型のオブジェクトを返す演算子」by @kinaba
• &envを使って、そのスコープで見えている変数のうち、指定した型がメタデータに付いているものを拾ってくる
(let [^File _ (File. "foo.txt") ^FileReader __ (FileReader. (the File)) ^BufferedReader ___ (BufferedReader (the FileReader))] (.readLine (the BufferedReader)))
inline assembler マクロhttp://www.slideshare.net/sohta/shibuyalisp-tt7
• Clojureコンパイラが使うバイトコード生成ライブラリをマクロ展開時に使う
(def fact (fn-iasm [n] (aload_1) (checkcast Integer) (invokevirtual ^int Integer/intValue []) (istore_2) (iconst_1) (istore_3) :loop (iload_2) (ifeq :end) (ilaod_2) (iload_3) (imul) (istore_3) (iinc 2 -1) (goto :loop) :end (iload_3) (invokestatic ^Integer Integer/valueOf [int]) (areturn)))
public final java.lang.Object invoke(java.lang. Code: Stack=2, Locals=6, Args_size=2 0: aload_1 1: checkcast #25; //class java/lang/I 4: invokevirtual #29; //Method java/lang/ 7: istore_2 8: iconst_1 9: istore_3 10: iload_2 11: ifeq 24 14: iload_2 15: iload_3 16: imul 17: istore_3 18: iinc 2, -1 21: goto 10 24: iload_3 25: invokestatic #33; //Method java/lang/ 28: areturn
syntactic-closurehttp://d.hatena.ne.jp/athos/20120506/syntactic_closure_in_clojure
• syntactic closuresによる健全なマクロを定義できるようにするライブラリ
• syntax-quoteで定義できないある種のマクロが定義可能に
低レベル
不健全
高レベル
健全
• 伝統的なマクロ
• syntactic closures• explicit renaming• syntax-case
• syntax-rules(define-syntax or [expr1 expr2] (sc-macro-transformer (fn [env] (quasiquote (let [val ~(make-syntactic-closure env nil expr1)] (if val val ~(make-syntactic-closure env nil expr2)))))))
(let [val true] (let [val396 false] (if val396 val396 val))
(let [val true] (or false val))
まとめ
• Clojureのマクロ周りには遊べるおもちゃがたくさん
• アイデア次第で貢献できる可能性?
• nagoya-lispに参加しましょう
おわり