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 没有分派宏对于: f clojure.lang.Util.runtimeException (Util.java:219)
`

原始的读取器代码没有区分没有数据读取器以及适当的读取器返回的 nil 值。因此它变得困惑,并试图找到一个分派宏,这使其进一步下到错误的代码路径,最终产生了误导性的错误信息。

原始的文档中没有将 nil 视为非法值。很明显,这个错误是原始数据读取器代码中的疏忽,而不是故意的行为。

这个补丁使用哨兵值来区分丢失的数据读取器情况与返回值 nil 的情况。

补丁: clj-1139-2.patch

15 个答案

0
by

评论由: [email protected] 制作

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

0
by

评论由: [email protected] 制作

clj-1138-data-reader-return-nil-for-no-op.patch 允许返回 nil 的 data-reader 被读取器视为无操作(like #_)。在 Clojure 1.4 到 1.5 beta2 期间,nil 通常不是一个有用的返回值(实际上它会导致异常)。使用这个补丁,可以使用数据读取器实现类似条件功能读取器的效果。

0

评论由: [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 值视为读取器的无操作(like #_)。

0

评论由: [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

评论者:jafingerhut

如果使用以下命令,则日期为 2012 年 12 月 22 日的补丁 clj-1138-allow-data-reader-to-return-nil-instead-of-throwing.patch 依然可以干净地应用到最新的 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

评论者:jafingerhut

现在,使用最新的 master(当时为 1.5.0-RC15),补丁 clj-1138-allow-data-reader-to-return-nil-instead-of-throwing.patch 已经无法干净地应用,甚至在上述 'git am' 命令中使用 --ignore-whitespace 选项。Steve,如果您能查看需要更新的内容,那将非常感谢。使用 "更新陈旧补丁" 部分中提出的补丁命令并不足够,因此可能需要手动仔细检查以查看哪些需要更新。

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]

_Nulla poena sine lege_ 或简言之,除非你说它是非法的,我应该能够做到。我的一个简单的例子是 {{#C NULL}},在我看来很明显就是 nil。

回顾这个问题,我发现大多数人认为带标签的字面量是 Clojure 字面量中编码 foreign values 的方式。如果你只关心一个可扩展的数据表示法,谁还需要另一种编写 nil 的方式呢?这是一个合理的问题。

我想使用数据读取器作为某种受限的读取宏(类似于 Common Lisp 中的使用)。我在做平台特定的某个任务时发现了这个 bug(在读取条件实现之前很久)。在我的情况下,在“其他”平台上返回 nil 是很方便的。

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

我会尝试在描述中添加更多上下文,但我不想让人们因为我不寻常的使用而忽视了明显的 bug(和糟糕的错误消息)。
0

评论由: [email protected] 制作

顺便说一下,这个 bug 是 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
...