Respo 中的 defcomp Macro

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

内容简介: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...

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

查看所有标签

猜你喜欢:

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

A Philosophy of Software Design

A Philosophy of Software Design

John Ousterhout / Yaknyam Press / 2018-4-6 / GBP 14.21

This book addresses the topic of software design: how to decompose complex software systems into modules (such as classes and methods) that can be implemented relatively independently. The book first ......一起来看看 《A Philosophy of Software Design》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

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

Markdown 在线编辑器

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具