44
Macros in Clojure @athos0220

Macros in Clojure

  • Upload
    sohta

  • View
    2.119

  • Download
    4

Embed Size (px)

Citation preview

Page 1: Macros in Clojure

 Macros in Clojure@athos0220

Page 2: Macros in Clojure

• Lispマクロ入門

• Clojureのマクロとその周辺

• Clojureマクロの応用例

Agenda

Page 3: Macros in Clojure

 Lispマクロ入門

Page 4: Macros in Clojure

マクロとは

構文木を組み替えるための仕組み→自由に構文を作ることができる仕組み

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)

Page 5: Macros in Clojure

マクロの定義• シンボルやリストで展開形のコードを作ってやる

(when ◯◯ △△ ✕✕)

(if ◯◯ (do △△ ✕✕) nil)

Page 6: Macros in Clojure

マクロの定義• シンボルやリストで展開形のコードを作ってやる

(when ◯◯ △△ ✕✕)

(if ◯◯ (do △△ ✕✕) nil)

(defmacro when [test & body] (list ’if test (cons ’do body) nil))

Page 7: Macros in Clojure

マクロの定義• シンボルやリストで展開形のコードを作ってやる

(when ◯◯ △△ ✕✕)

(if ◯◯ (do △△ ✕✕) nil)

(defmacro when [test & body] (list ’if test (cons ’do body) nil))

(defmacro when [test & body] `(if ~test (do ~@body) nil)

Page 8: Macros in Clojure

名前衝突の問題

Page 9: Macros in Clojure

名前衝突の問題束縛変数の衝突 自由変数の衝突

Page 10: Macros in Clojure

名前衝突の問題

(defmacro or [expr1 expr2] `(let [val ~expr1] (if val val ~expr2)))

束縛変数の衝突 自由変数の衝突

Page 11: Macros in Clojure

名前衝突の問題

(defmacro or [expr1 expr2] `(let [val ~expr1] (if val val ~expr2)))

(let [val true] (or false val))

束縛変数の衝突 自由変数の衝突

Page 12: Macros in Clojure

(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))

束縛変数の衝突 自由変数の衝突

Page 13: Macros in Clojure

名前衝突の問題

(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))

束縛変数の衝突 自由変数の衝突

Page 14: Macros in Clojure

名前衝突の問題

(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))

束縛変数の衝突 自由変数の衝突

Page 15: Macros in Clojure

名前衝突の問題

(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))

束縛変数の衝突 自由変数の衝突

Page 16: Macros in Clojure

(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))

束縛変数の衝突 自由変数の衝突

Page 17: Macros in Clojure

名前衝突の問題

(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))

束縛変数の衝突 自由変数の衝突

Page 18: Macros in Clojure

名前衝突の問題

(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))

束縛変数の衝突 自由変数の衝突

Page 19: Macros in Clojure

健全なマクロ (Hygienic macros)

• 名前の衝突を自動的に回避してくれるハイパーなマクロ

Page 20: Macros in Clojure

健全なマクロ (Hygienic macros)

• 名前の衝突を自動的に回避してくれるハイパーなマクロ

(define-syntax or (syntax-rules () ((or expr1 expr2) (let ([val expr1]) (if val val expr2)))))

Page 21: Macros in Clojure

健全なマクロ (Hygienic macros)

• 名前の衝突を自動的に回避してくれるハイパーなマクロ

(define-syntax or (syntax-rules () ((or expr1 expr2) (let ([val expr1]) (if val val expr2)))))

(let ([val true]) (or false val))

Page 22: Macros in Clojure

健全なマクロ (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)))

Page 23: Macros in Clojure

マクロの分類

低レベル

不健全

高レベル

健全

• 伝統的なマクロ

• syntactic closures• explicit renaming• syntax-case

• syntax-rules

• 高レベル:専用のパターン言語をもつ• 低レベル:Lispの関数でコードを操作する

Page 24: Macros in Clojure

マクロの分類

低レベル

不健全

高レベル

健全

• 伝統的なマクロ

• syntactic closures• explicit renaming• syntax-case

• syntax-rules

• 高レベル:専用のパターン言語をもつ• 低レベル:Lispの関数でコードを操作する

Page 25: Macros in Clojure

マクロの分類

低レベル

不健全

高レベル

健全

• 伝統的なマクロ

• syntactic closures• explicit renaming• syntax-case

• syntax-rules

>>

• 高レベル:専用のパターン言語をもつ• 低レベル:Lispの関数でコードを操作する

Page 26: Macros in Clojure

 Clojureのマクロシステムとその周辺

Page 27: Macros in Clojure

Clojureのマクロシステム

• 伝統的なマクロがベース

• syntax-quote が特徴

低レベル

不健全

高レベル

健全

• 伝統的なマクロ

• syntactic closures• explicit renaming• syntax-case

• syntax-rules

>>

Page 28: Macros in Clojure

syntax-quote

Page 29: Macros in Clojure

syntax-quote束縛変数の衝突 自由変数の衝突

Page 30: Macros in Clojure

(defmacro or [expr1 expr2] `(let [val# ~expr1] (if val# val# ~expr2)))

syntax-quote束縛変数の衝突 自由変数の衝突

Page 31: Macros in Clojure

syntax-quote束縛変数の衝突 自由変数の衝突

(defmacro or [expr1 expr2] `(let [val# ~expr1] (if val# val# ~expr2)))

syntax-quote

Page 32: Macros in Clojure

syntax-quote束縛変数の衝突 自由変数の衝突

(defmacro or [expr1 expr2] `(let [val# ~expr1] (if val# val# ~expr2)))

(let [val true] (or false val))

syntax-quote

Page 33: Macros in Clojure

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)

Page 34: Macros in Clojure

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)

Page 35: Macros in Clojure

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する

Page 36: Macros in Clojure

メタデータ• ほとんどすべてのオブジェクトにメタデータを付加できる

• Clojureではコードもデータ

→コードのほとんどあらゆる部分にメタデータをアノテーションとして付加できる

(defn ^:private f [x] x)

(def ^:dynamic x nil)

(defn fact ^long [^long x] (if (= x 0) 1 (* x (fact (- x 1)))))

名前をnamespaceにプライベートにする

動的スコープの変数にする 戻り値と引数の型ヒント

Page 37: Macros in Clojure

Java Interop

• ClojureからJavaのクラスにアクセスできる

• Clojureは大部分がJavaで書かれている

• Javaで書かれているClojureのコンパイラ自体や内部で使われる構文木にも触れる

Page 38: Macros in Clojure

暗黙の引数 &formと&env

• マクロ呼出しのフォームを囲むコンテキストに関する情報が渡ってくる

- &form:マクロ呼出しのフォーム全体

- &env:マクロ呼出しの時点で見えているローカル環境

Page 39: Macros in Clojure

 Clojure

マクロの応用例

Page 40: Macros in 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)))

Page 41: Macros in Clojure

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

Page 42: Macros in Clojure

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))

Page 43: Macros in Clojure

 まとめ

• Clojureのマクロ周りには遊べるおもちゃがたくさん

• アイデア次第で貢献できる可能性?

• nagoya-lispに参加しましょう

Page 44: Macros in Clojure

おわり