Macro 有點相似編譯過程執行的函數, 固然它不是函數,
不熟悉的能夠先看看下面的文章瞭解一下 Clojure 的 Macro:
https://learnxinyminutes.com/...html
;; 宏能夠傳入參數。 (defmacro inc2 [arg] (list + 2 arg)) (inc2 2) ; -> 4 ;; 不過,若是你嘗試配合使用引用列表,會致使錯誤, ;; 由於參數也會被引用。 ;; 爲了不這個問題,clojure提供了引用宏的另外一種方式:` ;; 在`以內,你可使用~得到外圈做用域的變量。 (defmacro inc2-quoted [arg] `(+ 2 ~arg)) (inc2-quoted 2)
我在 Respo 裏遇到一個語法糖的問題, 據說要 Macro 才能解決.
Respo 裏定義組件的寫法挺長的, 包含了好幾層的嵌套函數和縮進:函數
(def comp-demo (create-comp :demo (fn [content] (fn [cursor] (div {:class-name "demo-container" :style {:color :red}} (comp-text content nil))))))
不少代碼是重複的, 我想了下, 簡化之後甚至能夠這樣寫:post
(defcomp comp-demo [content] (div {:class-name "demo-container" :style {:color :red}} (comp-text content nil)))
本來我不知道有沒有辦法能作到, 畢竟 Respo 基於高階函數實現的,
固然, 對於用戶來講顯然是短的寫法更好記了,
除了沒有看到 cursor
定義可能會形成困擾以外, 能夠說是很好的寫法.code
後來看到社區其餘同窗的代碼, 發現是能夠作到的,
最終獲得的代碼是這樣的:htm
(defmacro defcomp [comp-name params & body] `(def ~comp-name (create-comp ~(keyword comp-name) (~'fn [~@params] (~'fn [~'cursor] ~@body)))))
要理解這個代碼, 須要對 Clojure 裏 Macro 的編寫有所瞭解defmacro
這個語法能夠定義 Macro, 後面是名字和參數, `()
表示括號的代碼都是符號, 不會被編譯器直接執行,~comp-name
表示其中的 comp-name
是通過計算的, 並非符號類型或者說代碼,~@body
跟上面相似, 可是加上 @
以後, 代表內容是序列, 能夠被展開,&
放在參數裏表示以後多個參數被摺疊到 body
變量上, 因此是序列,blog
爲了驗證這份代碼正常運行, 我用 boot repl
啓動一個 Clojure REPL:作用域
boot.user=> (defmacro defcomp [comp-name params & body] #_=> `(def ~comp-name #_=> (create-comp ~(keyword comp-name) #_=> (~'fn [~@params] #_=> (~'fn [~'cursor] ~@body))))) #'boot.user/defcomp
而後嘗試展開符號:get
boot.user=> (macroexpand-1 '(defcomp comp-demo [content] #_=> (div #_=> {:class-name "demo-container" #_=> :style {:color :red}} #_=> (comp-text content nil)))) (def comp-demo (boot.user/create-comp :comp-demo (fn [content] (fn [cursor] (div {:class-name "demo-container", :style {:color :red}} (comp-text content nil))))))
macroexpand-1
這個函數能夠將符號格式的代碼進行一次展開,
這裏的 '()
跟前面的 `()
是相似的, 表示這裏都是代碼.
而後我把返回結果格式化一下:編譯器
(def comp-demo (boot.user/create-comp :comp-demo (fn [content] (fn [cursor] (div {:class-name "demo-container", :style {:color :red}} (comp-text content nil))))))
基本上是同樣的, 不過 create-comp
被帶上了命名空間,
固然這個代碼運行不了, 由於 create-comp
沒定義, 可是至少符號嘛, 還不會報錯.fn
沒有被帶上命名空間, 是由於用了 ~'fn
寫法, 強行轉成了符合再轉回來.it
感興趣能夠扒更多文章看(雖然下面的文章我只看了一部分...)
http://lambdax.io/blog/posts/...
http://www.braveclojure.com/w...
https://aphyr.com/posts/305-c...