Respo 中的 defcomp Macro

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

相关文章
相关标签/搜索