【发布时间】:2017-10-22 04:08:23
【问题描述】:
我对 Clojure 比较陌生,无法完全理解阅读器宏和常规宏之间的差异,以及差异为何如此显着。
在什么情况下你会使用其中一种,为什么?
【问题讨论】:
我对 Clojure 比较陌生,无法完全理解阅读器宏和常规宏之间的差异,以及差异为何如此显着。
在什么情况下你会使用其中一种,为什么?
【问题讨论】:
阅读器宏以普通宏无法做到的方式更改语言的语法(例如,@foo 变成 (deref foo))(普通宏无法去掉括号,所以你必须做类似(@ foo)) 的事情。它被称为 reader 宏,因为它是在 repl 的 read 传递中实现的(查看 source)。
作为 clojure 开发人员,您只会创建 常规 宏,但会使用大量阅读器宏,而不必明确考虑它们。
阅读器宏的完整列表在这里:https://clojure.org/reference/reader,包括常见的东西,如@',和#{}。
Clojure(与其他一些 lisps 不同)不支持用户定义的阅读器宏,但通过 tagged literals(例如 #inst 或 #uuid)在阅读器中内置了一些可扩展性
【讨论】:
tl;dr*
宏 [普通宏] 在求值期间扩展(REPL 的 E),与符号相关联,对 lisp 对象进行操作,并出现在表单的第一个或“函数”部分中。 Clojure 和所有 lisps 都允许定义新的宏。
Reader 宏在读取期间运行,在求值之前,是单个字符,对字符串进行操作,在所有 lisp 对象从 reader 发出之前,并且不限于在第一个或“函数”中,表格的一部分。 Clojure 与其他一些 lisps 不同,它不允许定义新的阅读器宏,除非编辑 Clojure 编译器本身。
更多词:
普通的非阅读器宏,或者只是“宏”,对 lisp 对象进行操作。考虑:
(and 1 b :x)
and 宏将使用两个值调用,一个值是 1,另一个是由符号 b(不是 b 的值)和关键字 :x 组成的列表。 and 宏正在处理的所有内容都已经是一个 lisp (Clojure) 值。
宏扩展仅在宏位于列表开头时发生。 (and 1 2) 扩展了 and 宏。 (list and) 返回错误,“Can't take value of a macro”
阅读器负责将字符串转换为在 Clojure 中,阅读器宏是单个字符,它改变阅读器(负责将文本流转换为 lisp 对象的部分)的操作方式。 Clojure 的 lisp 阅读器的调度位于 LispReader.java。正如 Alejandro C. 所说,Clojure 不支持添加阅读器宏。
阅读器宏是一个字符。 (我不知道这是否适用于所有 lisps,但 Clojure 当前的实现只支持单字符阅读器宏。)
阅读器宏可以存在于表单中的任何位置。考虑(conj [] 'a),如果' 宏是正常的,则tick 需要成为一个lisp 对象,因此代码将是符号conj、空向量、符号' 以及最后的符号@ 的列表987654336@。但是现在评估规则将要求 ' 自行评估。相反,读者在看到 ' 时会包装完整的 s-exp,然后是 quote,因此返回给评估器的值是 conj 的列表、一个空向量和 quote 的列表通过a。现在quote 是列表的头部,可以更改它引用的评估规则。
【讨论】:
简短地说,阅读器宏是一个低级功能。这就是为什么他们很少(只是@,退出等等)。拥有许多阅读规则会使任何语言变得一团糟。
常规宏是 Clojure 中广泛使用的一种工具。作为开发人员,如果您不是 Clojure 核心开发人员,欢迎您编写自己的常规宏,但不要编写阅读器宏。
您可以始终使用自己的标记文字来替代阅读器规则,例如 #inst "2017" 会给您一个 Date 实例等等。
【讨论】: