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的读取,那么请使用不带eval的读取,例如使用引号

'#example/symbolize "hello"

?

REPL在这里似乎是正交的。您可以看到,即使它是符号,它也没有eval `edn/read-string`的输出。我在发布之前也在clj文件的源代码中测试了这个字面量,结果相同。
源文件也会被读取和评估,所以这与问题完全相关。
这里有一些额外的背景知识:[点此查看](https://clojure.org/guides/learn/syntax#_evaluation)
感谢在周日回复。我的体会是,标记字面量会进行eval,因此与标记元素的工作方式大不相同。不知何故,我从文档的表述中一点也没有看出这一点。

现在我理解了,标记字面量与标记元素之间有一个截然不同的接口。我可以尽力告诉人们,“如果你使用我在edn中制作的这个标签,并使用我的库读取它,它会像这样工作,但如果你试图在你的源中使用这个字面量,你需要像这样加引号”。我只是认为这两个接口的差异很大,但我想这可能是不言而喻的。我相信一定有我从未考虑过的用例。

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

我理解从安全性的角度,但最终得到的接口与标记化字面量不同。我天真地预期,由于edn明确提到了关于标记化字面量的读取器参考资料,这两者在“读取”的意义上应该是相同的,尤其是考虑到从 eval-ing 标记化字面量获得的好处似乎非常小,所以人们会倾向于选择接口一致性。但是,这就是我回到说肯定还有很多我没有考虑到的用例。这里并不是在抱怨,只是解释为什么这对我是个绊脚石。
by
不,标记化字面量和标记化元素是同一回事。它们是读取时常量结构。是否评估取决于上下文。REPL中的代码和源文件中的代码都是读取和评估的。read和read-string将读取(但不进行评估)。引用是另一种读取但不进行评估的技术。这里没有区别——一切都取决于如何使用。
by
也许我们在周末的末尾讨论得有点过去了。

我的意思是,如果你在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中或正在加载命名空间源文件,或者特别是在您的编辑器中求值某个形式)。

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