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在这两种情况下都使用相同的读取。如果你想进行不带eval的读取,请使用引号进行读取,例如。

'#example/symbolize "hello"

by
?

REPL在这里似乎是正交的。你可以看到,即使它是一个符号,它也没有对`edn/read-string`的输出进行eval。我之前也在发布之前测试了cloj文件的源中的字面量,结果相同。
by
源文件也会被读取和评估,所以这是完全相关的。
by
关于这个问题的更多背景信息:[链接](https://clojure.org/guides/learn/syntax#_evaluation)
by
感谢您在星期天回复。我的理解是,标记字面量与标记元素之间有一个截然不同的接口。我可以竭尽全力地向人们说明:“如果你使用我在edn中制作的这个标签并使用我的库进行读取,它会像这样工作,但如果你想在源文件中使用这个字面量,你需要像这样进行引号注释。”我只是认为这两个接口的差异太大,但不言而喻。我确信还有许多我没有考虑到的用例。

现在我明白了标记字面量与标记元素之间的接口有多么不同。我可以尽力向人们说明:“如果你使用我在edn中制作的这个标签并使用我的库进行读取,它会像这样工作,但如果你想在源文件中使用这个字面量,你需要像这样进行引号注释。”我只是觉得这两个接口的差异太大,但这可能不言而喻。我确信还有很多我未曾考虑到的用例。

再次感谢。
换个说法——尽管有“读取”这个词,使用 `edn/read-string` 总是最终得到一个数据结构(或对象),这就是全部。它是读取进来的,但是只读取数据——真正的读取器会读取产生符号以供立即自动评估(对吧?)并且针对每个 ` clojure.core/read` 运行 eval,不只是给你数据?

我理解这是从安全的角度考虑的,但你得到的接口与标记字面量不同。我天真地期望由于 edn 明确指出了讨论标记字面量的读取器引用,所以这两个都应该“读取”在相同的意义上,特别是考虑到从 eval 标记字面量的潜在优势如此微小,以至于我们倾向于选择接口一致性。但这正是我回到说当然有许多我没有考虑到的用例。在这里没有抱怨,只是解释这对我来说是一个障碍。
不,标记字面量和标记元素是同一事物。它们是读取时的构造。它们是否被评估取决于上下文。在 repl 和源代码中的代码会被读取和评估。read 和 read-string 会读取但不评估。引用是另一种读取但不评估的技术。这里没有差别——这一切都取决于你怎么使用它。
也许我们在周末结束的时候在浪费时间。

我想表达的是,如果你在一个 edn 文件中使用给定的标签 X 和链接到函数 Y,然后用它读取并传递给 `edn/read-string`,你得到未评估的符号。如果你在使用标记字面量中使用同样的 X/Y 组合,你会得到已评估的符号。基本上就是上面我的 repl 会话中显示的内容。

在我看来,这是两个不同的接口,也许我们在这里对术语有不同的理解。“你得到的东西取决于上下文”——我同意你的说法。我明白如果我使用 ` clojure.core/read-string`,我会得到另一种行为。我并不是在抱怨这种不同,只是在注意这一点。

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

亚历克斯是对的,我认为你可能误解了他的回答。看看这个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参考中)标签化字面量在读取时应用,所以当然它们会被求值——不能只是将它们作为数据。而标签化元素——在读取edn文件时——将通过显式的读取调用来在运行时消费,因此除非你特意做,否则它们不会求值。

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

(这里有一段更长、更混乱的评论,我将用这个替换!)
我想说的唯一一点是,它们都是“标签化字面量”。读取器——无论是EDN读取器还是Clojure读取器——都会读取标签之后的表单(因此它必须是有效的EDN或Clojure数据),然后在该表单上调用指定的函数:这是作为读取过程的纯粹符号评估的一部分。

如果上下文意味着一个在读取之后将被评估的表单,那么读取器产生的符号表单将被评估。

这不仅仅适用于标签化字面量。读取器将文本转换为(EDN或Clojure)表单。结果是表单可能是可以评估的(如果你在REPL中,或在加载某个命名空间源文件时,或在编辑器中特别评估某个表单)。

在许多方面,这与宏的工作方式相似:读取器将文本转换为表单,符号表单被传递给宏(函数),该函数返回一个新的符号表单,并且如果上下文需要的话,该新表单将被评估。
...