Respo 中的 defcomp Macro

栏目: 编程语言 · Clojure · 发布时间: 7年前

内容简介:Respo 中的 defcomp Macro

Macro 有点类似编译过程执行的函数, 当然它不是函数,

不熟悉的可以先看看下面的文章了解一下 Clojure 的 Macro:

https://learnxinyminutes.com/...
;; 宏可以传入参数。
(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))))))

很多代码是重复的, 我想了下, 简化以后甚至可以这样写:

(defcomp comp-demo [content]
  (div
    {:class-name "demo-container"
     :style {:color :red}}
    (comp-text content nil)))

原本我不知道有没有办法能做到, 毕竟 Respo 基于高阶函数实现的,

当然, 对于用户来说显然是短的写法更好记了,

除了没有看到 cursor 定义可能会造成困扰之外, 可以说是很好的写法.

后来看到社区其他同学的代码, 发现是可以做到的,

最终得到的代码是这样的:

(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 变量上, 所以是序列,

为了验证这份代码正常运行, 我用 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

然后尝试展开符号:

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 写法, 强行转成了符合再转回来.

感兴趣可以扒更多文章看(虽然下面的文章我只看了一部分...)

http://lambdax.io/blog/posts/...

http://www.braveclojure.com/w...

https://aphyr.com/posts/305-c...

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Java解惑

Java解惑

布洛赫、加夫特 / 陈昊鹏 / 人民邮电出版社 / 2006-1 / 39.00元

本书特写了95个有关Java或其类库的陷阱和缺陷的谜题,其中大多数谜题都采用了短程序的方式,这些程序的行为与其看似的大相径庭。在每个谜题之后都给出了详细的解惑方案,这些解惑方案超越了对程序行为的简单解释,向读者展示了如何一劳永逸地避免底层的陷阱与缺陷。 本书趣味十足、寓教于乐,适合于具备Java知识的学习者和有编程经验的Java程序员。一起来看看 《Java解惑》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器