Clojure宏嵌套。空指针异常
我对Clojure很陌生,我想,一般来说,编程都很陌生,但我最终尝试编写某种DSL来生成PDF文件(使用PDFBox,一个java库)。 我希望最后的语法如下所示:Clojure宏嵌套。空指针异常,clojure,macros,Clojure,Macros,我对Clojure很陌生,我想,一般来说,编程都很陌生,但我最终尝试编写某种DSL来生成PDF文件(使用PDFBox,一个java库)。 我希望最后的语法如下所示: (document {:name "test" :bleed "22mm" ...} (page {:height "560mm" ...) (text {:color "red"} "blabla") (line-break)) 所以问题是,每个表达式都是一个宏,它将在eval time做一些事情+递归地读取第一个参数后的
(document {:name "test" :bleed "22mm" ...}
(page {:height "560mm" ...) (text {:color "red"} "blabla") (line-break))
所以问题是,每个表达式都是一个宏,它将在eval time做一些事情+递归地读取第一个参数后的每个表达式,并使用conj
的选项作为第一个参数展开它。例如,现在看起来是这样的:
(defmacro document
[{:keys [name] :as options}
& body]
`(let [~'pdoc ~'(PDDocument.)]
(do
~@(unfold (conj options {:pdoc 'pdoc}) ~body)
(doto ~'pdoc
(...)))
和展开宏(展开参数的宏):
问题是我无法让他们一起工作。经过一些调试后,我仍然得到java.lang.NullPointerException:
我感觉到问题来自展开输出到文档的方式,但我完全无法了解宏嵌套时的反应。当我使用macroexpand
时,嵌套宏永远不会展开,我不知道这是预期的还是编程错误。不管怎样,什么时候
(macroexpand '(unfold {...} (document) (document)))
我明白了
在我目前的理解中,这似乎是可以的,因为我在之后使用它。但我显然遗漏了什么
---------编辑-----------
为了一个非常无用的
(document {:name "hey"}} (document {:name "ho"}))
最后我想以((doto…
为例):
很抱歉,这篇文章很具体,篇幅很长,但我不知道如何解释这个问题,而没有描述所有内容
提前谢谢大家,,
再见我不确定这是否能解决问题,但有几点建议:
- 对于
let
绑定,请使用foo 35;
而不是~'foo
以避免命名冲突。末端的锐利会生成随机的机器符号,因此您的范围内没有隐藏的bug李>
- 尝试使用
(macroexpand'(文档{…}))
展开宏定义,查看其中的内容。您可以在REPL中重新评估结果列表以检查其是否存在错误
- 最后,暂时避免使用宏。首先尝试用简单的函数实现您的想法。可能足够了
我不确定这是否能解决问题,但有几点建议:
- 对于
let
绑定,请使用foo 35;
而不是~'foo
以避免命名冲突。末端的锐利会生成随机的机器符号,因此您的范围内没有隐藏的bug李>
- 尝试使用
(macroexpand'(文档{…}))
展开宏定义,查看其中的内容。您可以在REPL中重新评估结果列表以检查其是否存在错误
- 最后,暂时避免使用宏。首先尝试用简单的函数实现您的想法。可能足够了
感谢大家的帮助和建议。
现在,我喜欢这样的东西 :
(defn unfold
[options body]
(loop [
[f & fs] body
opts options
output []]
(if (empty? f)
output
(recur fs
(conj opts (second f))
(conj output (list (first f) (conj opts (second f)))))))))
及
它感觉有点粗糙,但它的工作原理和预期的一样。
无论如何,我现在将尝试研究新的方法和策略。感谢大家的帮助和建议。
现在,我喜欢这样的东西 :
(defn unfold
[options body]
(loop [
[f & fs] body
opts options
output []]
(if (empty? f)
output
(recur fs
(conj opts (second f))
(conj output (list (first f) (conj opts (second f)))))))))
及
它感觉有点粗糙,但它的工作原理和预期的一样。
我现在将尝试研究新的方法和策略。您可以添加一个对宏的示例调用,并提供所需的输出吗?您调用的unfold
就像一个函数,但它是一个宏。发出展开
(需要发出执行
才能工作)或使展开
成为功能。另外,请添加整个异常消息。@clojuremotly-Hum我想我明白了。你更喜欢哪种方式?让它成为一个功能似乎更干净。这就是整个异常信息。奇怪的是它是空的…我都见过,所以这是你的偏好。如果需要在代码中使用展开
,则保留宏。如果展开
仅用于库生成代码,那么一定要将其作为函数。仅供参考:一个宏有两个隐式参数,在像函数一样调用它时不会传入。因此(可能)出现了错误。另外,您似乎不希望unfold
成为一个vararg?您可以添加一个对宏的示例调用,以获得所需的输出吗?您调用unfold
就像调用一个函数,但它是一个宏。发出展开
(需要发出执行
才能工作)或使展开
成为功能。另外,请添加整个异常消息。@clojuremotly-Hum我想我明白了。你更喜欢哪种方式?让它成为一个功能似乎更干净。这就是整个异常信息。奇怪的是它是空的…我都见过,所以这是你的偏好。如果需要在代码中使用展开
,则保留宏。如果展开
仅用于库生成代码,那么一定要将其作为函数。仅供参考:一个宏有两个隐式参数,在像函数一样调用它时不会传入。因此(可能)出现了错误。而且,您似乎不想unfold
成为一个vararg?
(let [pdoc (.PDDocument)]
(do
(let [pdoc (.PDDocument)]
(do
(doto pdoc .save (str "ho" ".pdf"))))
(doto pdoc .save (str "hey" ".pdf"))))
(defn unfold
[options body]
(loop [
[f & fs] body
opts options
output []]
(if (empty? f)
output
(recur fs
(conj opts (second f))
(conj output (list (first f) (conj opts (second f)))))))))
(defmacro document
[{:keys [name] :as options}
& body]
`(let [~'pdoc ~'(PDDocument.)]
(do
~@(map macroexpand
(unfold (conj options {:pdoc 'pdoc}) body))
(doto ~'pdoc
(...)))))