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

欢迎!请查看关于页面以了解更多关于该系统的工作原理的信息。

+1投票
Clojure

读取器参考文档声明:“标签字面量是Clojure对edn标签元素的实现。”

但在至少一点上两者是不同的:由标签字面量产生的符号将被解析,而由标签元素产生并由clojure.edn/read-string读取的符号则不会。

这意味着如果您读取包含给定标签的EDN与如果使用相同的标签在您的源中生产字面量,您可能会得到不同的输出。

示例

data_readers.cj

{example/symbolize clojure.core/symbol}

然后

user> (edn/read-string {:readers {(quote example/symbolize) clojure.core/symbol}}
                       "#example/symbolize\"hello\"")   
hello
user> #example/symbolize "hello"
Syntax error compiling at (*cider-repl clojure/foragr:localhost:46533(clj)*:0:0).
Unable to resolve symbol: hello in this context
user>

这种差异是否有特定的原因?有没有在从标签字面量解析符号时是好的场景?如果是这样,为什么不从标签元素和edn/read-string中做同样的事情呢?

2回答

+1投票

被选择
 
最佳答案

第一个只是被读取。
第二个是被读取然后评估(因为R E PL)

所以,没有差异。REPL在这两种情况下都使用相同的读取方式。如果您想要无评估的读取,那么就使用无评估的读取,例如通过承诺

'#example/symbolize "hello"

 
?

在这里,repl似乎与这个问题无关。你可以看到它甚至没有评估`edn/read-string`的输出,尽管它是一个符号。我在发表之前也测试了clj文件中的字面量源代码,结果是一样的。
 
源文件也被读取并评估,所以这是完全相关的。
 
这里有一些额外的背景信息: https://clojure.org/guides/learn/syntax#_evaluation
 
感谢你在周日回帖。我的看法是,标记字面量会被评估,因此与标记元素大不相同。不知为何,我完全没有从文档中理解这一点。

现在我明白标记字面量与标记元素确实接口不相同。我可以尽力告诉人们,“如果你使用我在edn中制作的这个标签并使用我的库读取它,它会像这样工作,但如果你试图在你的源中使用这个字面量,你需要像这样引用它”。我只觉得这两种接口的差异很大,但我猜这一点也许是理所当然的。我确信存在我从未考虑过的用例。

再次感谢。
换句话说 - 尽管有“读取”这个词,使用`edn/read-string`总是得到一个数据结构(或对象),句号。它被读进来了,但只有数据 - 一个真正的读取器将会产生符号供即时自动评估(对吗?)并且为每个`clojure.core/read`执行eval,而不仅仅是给你数据?

我从安全角度理解这一点,但你得到的界面与标记文字不同。我天真地期望,由于edn在讨论标记文字的读取器引用中明确提及,这两个 urlString 会“读取”得一样,尤其是由于评估标记文字的潜在优势似乎很小,人们往往会选择界面一致性。但这就是我再次说我认为有很多我没有考虑到的用例的原因。在这里不是抱怨,只是在解释为什么这对我的速度是一个障碍。
不,标记文字与标记元素是同一件事。它是在读取时的构造。它们是否被评估取决于上下文。repl和源文件中的代码将被读取和评估。read和read-string将读取(但不评估)。引用是另一种读取但不评估的技术。这里没有区别 - 这一切都取决于你怎么用。
也许在周末末我们说的不是一回事。

我想说的是,如果你在一个edn文件中使用关联标签X和函数Y,并用`edn/read-string`读取并给它,你会得到未评估的符号。如果你在标记文字中使用这个X/Y组合,你会得到已评估的符号。基本上,就像我上面的repl会话中展示的那样。

从我来看,这是两个不同的界面,但我可能我们在术语上有所不同。“你得到的取决于上下文” - 我同意你的观点。我意识到,如果我使用`clojure.core/read-string`,我会得到另一种行为。我并不是在抱怨这种差异,只是在指出它。

顺便说一下,我不期望你会“同意”我上面所说的,我已经占用你足够的时间了,请享受剩下的周末,再次感谢。
+1投票

Alex 没错,我觉得你可能误解了他的回答。看看这个 REPL 会话是否能让你信服

(~/clojure)-(!2003)-> cat src/data_readers.clj
{example/symbolize clojure.core/symbol}

Sun Aug 27 17:02:45
(~/clojure)-(!2004)-> clj
Clojure 1.12.0-alpha4
user=> (require '[clojure.edn :as edn])
nil
user=> (edn/read-string {:readers {(quote example/symbolize) clojure.core/symbol}}
                       "#example/symbolize\"hello\"")
hello ; edn/read-string produces a symbol
user=> (read-string "#example/symbolize\"hello\"")
hello ; core/read-string also produces a symbol
user=> hello ; this symbol is not bound to anything
Syntax error compiling at (REPL:0:0).
Unable to resolve symbol: hello in this context
user=> #example/symbolize "hello"
;; Read (produces symbol hello) Eval (tries to lookup the symbol's value and fails)
Syntax error compiling at (REPL:0:0).
Unable to resolve symbol: hello in this context
user=> #example/symbolize "\"hello\""
Syntax error compiling at (REPL:0:0).
;; Note the subtle difference in the error: Read produced a SYMBOL again, spelled "hello"
;; and there's no bound "hello" symbol either
Unable to resolve symbol: "hello" in this context
user=>

编辑了
嗨 Sean,谢谢你。

我认为我之前没有注意到的一个区别是:当然,这些在 clojure reader reference 中有文档说明,标记字面量在读取时应用,所以当然它们会被求值 - 无法将它们仅保留为数据。而标记元素 - 在你正在读取的 edn 文件的上下文中 - 将在运行时通过显式的读取调用消耗掉,所以除非你特意这样做,否则它们不会求值。

我认为看到您的 clojure.core/read-string 示例有助于阐明这一点,所以谢谢!

(此处有一段更长的、更困惑的评论,我已经用这个取代了!)
在这里的唯一评论是它们都是 "标记字面量"。读取器 - 不论是 EDN 读取器还是 Clojure 读取器 - 将读取标签后的形式(所以它必须是有效的 EDN 或 Clojure 数据),然后对该形式调用指定的函数:这是读取过程中的纯粹符号求值。

如果上下文是这样的,那么在读取后将会求值的形式,读者产生的符号形式将会被求值。

这不仅仅针对标记字面量。读取器将文本转换为(EDN 或 Clojure)形式。这些形式可能被求值(如果你处于 REPL 或加载命名空间源文件,或在编辑器中特别求值一个形式)。

在许多方面,这与宏的工作方式相似:读取器将文本转换为形式,符号形式被传递到宏(函数),该函数返回一个新的符号形式,而这个新形式如果需要就会被求值。
...