2024 Clojure 状态调查!中分享您的想法。

欢迎!请参阅关于页面,了解更多这个工作方式的信息。

+1
语法和读取器

如果数据读取器返回 nil,读取器将抛出 java.lang.RuntimeException: No dispatch macro... 错误消息表明,对于标签的第一个字符,没有相应的分派宏。

这里有一个简单的例子

`
user=> (binding [*data-readers* {'f/ignore (constantly nil)}}

     (read-string "#f/ignore 42 10"))

RuntimeException No dispatch macro for: f clojure.lang.Util.runtimeException (Util.java:219)
`

原始的读取器代码没有区分数据读取器的缺失和合适的读取器返回 nil 的情况。因此它变得困惑,并尝试查找分派宏,导致其沿着错误的代码路径进一步下去,最终产生误导性的错误信息。

原始文档中没有区分 nil 作为非法值。很明显,这个错误是原始数据读取器代码中的疏忽,而不是有意功能。

该补丁使用哨兵值将缺失数据读取器的情况与返回值 nil 的情况区分开来。

补丁: clj-1139-2.patch

15 个答案

0

评论者:[email protected]

clj-1138-allow-data-reader-to-return-nil-instead-of-throwing.patch 允许数据读取器返回 nil 而不是抛出异常。进行适当的检查,检查可能的标签或记录是否是JavaIdentifierStart()。对于可能是分派宏的特殊字符提供更好的错误信息(而不是假设它是标签字面量)。

0

评论者:[email protected]

clj-1138-data-reader-return-nil-for-no-op.patch 允许将数据读取器返回的 nil 值视为一个不做任何操作(no-op,类似于 #_)由读取器处理。通常,nil 不是一个有意义的值(实际上,在 Clojure 1.4 到 1.5 beta2 之间,它会导致抛出异常)。有了这个补丁,就可以使用数据读取器实现类似条件功能读取器。

0
by

评论者:[email protected]

clj-1138-allow-data-reader-to-return-nil-instead-of-throwing.patch 是考虑的第一个补丁。它仅仅允许数据读取器返回 nil 值,并将 nil 作为最终返回值。我认为它实现了最初为分派宏设计的意图,并且在许多情况下(大多数是打字错误)提供了更好的错误信息。

第二个补丁,clj-1138-data-reader-return-nil-for-no-op.patch,依赖于先应用其他补丁。它采取额外的一步,将数据读取器返回的 nil 值视为对于读取器(像 #_)的不做操作。

0
by

评论者:[email protected]

最终发现,您可以通过让数据读取器返回 (quote nil) 代替简单的 nil 来绕过原始问题。该表达式可以方便地计算出 nil,这样在必要时就可以得到 nil。在应用补丁后,这也仍然有效,因此如果您真的想返回 nil,仍然有这个途径。

(binding (link: *data-readers* {'x/nil (constantly '(quote nil))}) (read-string "#x/nil 42"))
;=> (quote nil)
0
by

评论者:jafingerhut

如果使用以下命令,则 clj-1138-allow-data-reader-to-return-nil-instead-of-throwing.patch(日期为 2012 年 12 月 22 日)仍然能够在最新的 master 上干净地应用:

% git am --keep-cr -s --ignore-whitespace < clj-1138-allow-data-reader-to-return-nil-instead-of-throwing.patch

如果没有使用 --ignore-whitespace 选项,则补丁应用失败,原因是最近在 Clojure master 中进行了某些空白字符的更改。

0
by

评论者:jafingerhut

好,现在有了最新的 master(1.5.0-RC15),clj-1138-allow-data-reader-to-return-nil-instead-of-throwing.patch 无法再干净地应用,即使在上述 'git am' 命令中使用 --ignore-whitespace 选项也是如此。Steve,如果你能看看需要更新什么,那会很好。使用 "Updating stale patches" 部分中提出的补丁命令不够,因此可能需要手动仔细检查以确定需要更新什么。

0

评论者:[email protected]

我移除了我的补丁。最近LispReader和新的EdnReader发生了变化。

0

评论由:alexmiller

修复了空白符警告,并更新了补丁以使其适用,没有语义上的更改,在 clj-1139-2.patch 中保留了归属。

0

评论由:alexmiller

票据需要更好的问题描述和补丁中采取的方法。

0

评论者:[email protected]

如果问题不清楚,我会问为什么一个 nil 返回值会作为数据读取器的特别处理?如果它被视为设计上的非法,这条错误信息会对用户有什么启示?

我在提交错误时没有找到任何文档化的限制,我今天也是如此。所以这对我来说似乎是一个简单的错误。数据读取器应该允许返回 nil,Clojure 读取器应该像平常一样处理 nil。我的解决方案是返回 (quote nil),这给出了预期的行为,并且没有触发错误。

0

评论由:alexmiller

我希望对描述有更多的更新。我的问题是调用数据读取器函数是否应该始终返回 nil。有没有什么好的用例需要这样做?这看起来你正在使用读取器读取非 nil 标签名值,因此返回 nil 是令人生疑的。这不可能进行来回传递,因此似乎是不对称的。

0
_评论由:[email protected]_

没有法律规定的惩罚,也就是说,除非你说它是非法的,否则我应该能够做到。我的简单例子是 {{#C NULL}},对我来说这似乎是一个非常明显的空值。

回顾这个问题,我发现大多数人认为标记字面量是将外部值编码到Clojure字面量中的方法。如果你只关心可扩展的数据表示法,谁还需要另一种写nil的方法?这是一个公平的问题。

我原本想使用数据读取器作为某种受限的读取宏(如Common Lisp中使用的)。我在做某些平台特定的事情时发现了这个错误(在读取条件实现之前很久)。在我的情况下,在其他平台上返回nil很方便。

数据读取器的许多用法不是双射的。例如,{{#infix (3 + 4)}} 作为一个常量7解释,同样也不是可逆的。除非你是Dan Friedman或Wil Byrd,否则可逆性是一个棘手的要求。:-)

我会尝试更新我的描述并增加更多的上下文,但不希望因为我的非常规使用而分散大家对明显错误的注意力(以及错误的错误信息)。
0

评论者:[email protected]

顺便说一下,这个错误是CLJ-1138,但提出的补丁说“1139”,这可能会让一些忙碌的审阅者感到困惑。

0
_评论由:[email protected]_

我测试了这个补丁,并在当前的master上运行良好。我建议添加另一个测试来确认edn/read-string也正确地工作。这是我使用的。这也测试了覆盖默认读取器是否有效。如果您需要,请随意使用这个测试。


(deftest clj-1138-uuid-override
  (is (nil? (binding [*data-readers* {'uuid (constantly nil)}]
              (read-string "#uuid \"550e8400-e29b-41d4-a716-446655440000\""))))
  (is (nil? (edn/read-string {:readers {'uuid (constantly nil)}}
                             "#uuid \"550e8400-e29b-41d4-a716-446655440000\""))))

0
...