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在这里似乎是其自身的正交部分。您可以看到尽管它是一个符号,但它并没有对`edn/read-string`的输出进行eval操作。我在发布前也对clj文件源中的字面量进行了测试,结果是相同的。
源文件也会被读取和评估,因此这一点非常相关。
以下是一些额外的背景信息:[链接](https://clojure.org/guides/learn/syntax#_evaluation)
感谢您在周日回复。我的体会是,标记字面量和标记元素有着完全不同的接口。我可以尽力告诉人们,“如果你使用我创建的这个edn标签并使用我的库读取它,它的工作方式是这样的,但如果你在你的源中尝试使用这个字面量,你需要像这样引用它。”我觉得这两个接口的差异性不高,但我想这可能是显而易见的。我相信还有一些我从未考虑过的用例。

现在我理解了键别名和键元素之间接口的巨大差异。我可以说,“如果你使用我在edn中创建的这个标签,并且使用我的库阅读它,它的表现像这样,但如果你在源中使用这个字面量,你需要像这样引用它。”我觉得这两个接口的差异很大,但我想这一点可能不言自明。我相信有我从未考虑过的用例。

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

我从安全角度理解这一点,但你得到的接口和标记文字不同。我天真地预期,由于edn被明确引用在讨论标记文字的读取器参考中,这两个将“读取”在相同的意义上,尤其是因为评估标记文字的潜在好处如此之小,以至于人们往往会选择接口一致性。 但这就是我回到说肯定有许多我没有考虑到的用例。这里没有抱怨,只是解释这对我来说为什么是一个障碍。
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阅读器参考中有文档),标记字面量在读取时应用,因此当然会被求值 - 不可能仅仅将它们作为数据留下。而标记元素(在读取edn文件时)则会在运行时通过显式的读取调用消耗,因此除非你特意这样做,否则不会求值。

看到你提供的clojure.core/read-string示例,我对此有了更清晰的认识,所以谢谢!

(这里有一条更长的、更困惑的评论,我已经用这个来替换了!)
在这里我要说的唯一一件事是,它们都是“标记字面量”。阅读器(无论是EDN阅读器还是Clojure阅读器)都会读取标记之后的格式(因此它必须是有效的EDN或Clojure数据),然后对该格式调用指定的函数:纯粹作为读取过程的一部分的符号评估。

如果在某个上下文中,格式在读取之后会被评估,那么由阅读器产生的符号格式将会被评估。

这并不仅限于标记字面量。阅读器将文本转换为(EDN或Clojure)格式。生成的格式可能会被评估(如果你在REPL中,或者正在加载命名空间源文件,或者是从你的编辑器中特别评估格式)。

在许多方面,这类似于宏的工作方式:阅读器将文本转换为格式,符号格式被传递到宏(函数)中,宏(函数)返回一个新的符号格式,如果上下文需要,则对该新格式进行评估。
...