我不清楚所有这些读字符串函数之间的关系.好吧,很明显clojure.core/read-string
可以读取由pr[n]
或甚至输出的任何序列化字符串print-dup
.很明显,clojure.edn/read-string
读取根据EDN规范格式化的字符串.
但是,我从Clojure脚本开始,并不清楚是否cljs.reader/read-string
遵守.这个问题是由于我有一个Web服务正在发出以这种方式序列化的clojure代码:
(with-out-str (binding [*print-dup* true] (prn tags)))
那就是产生包含数据类型的对象序列化.但是,这是不可读的cljs.reader/read-string
.我总是得到这种类型的错误:
Could not find tag parser for = in ("inst" "uuid" "queue" "js") Format should have been EDN (default)
起初,我认为这个错误被抛出cljs-ajax
但是在测试了cljs.reader/read-string
一个rhino REPL后,我得到了同样的错误,这意味着它cljs.reader/read-string
本身就被抛出了.它是由maybe-read-tagged-type
函数引发的,cljs.reader
但不清楚这是因为读者只能使用EDN数据,还是......?
另外,根据与Clojure文档的差异,唯一可以说的是:
The read and read-string functions are located in the cljs.reader namespace
这表明他们应该完全具有相同的行为.
简介:Clojure是EDN的超集.默认情况下,pr
,prn
和pr-str
,给Clojure的数据结构时,产生有效的EDN.*print-dup*
改变这一点并使它们使用Clojure的全部功能,以便在往返后更好地保证内存中对象的"相同性".ClojureScript只能读取EDN,而不是完整的Clojure.
简单的解决方案:不要设置*print-dup*
为true,只将纯数据从Clojure传递给ClojureScript.
更难的解决方案:使用带标记的文字,两边都有(可能是共享的)相关阅读器.(但这仍然不会涉及*print-dup*
.)
切向相关:大多数用于EDN的用例由Transit覆盖,这更快,特别是在ClojureScript方面.
让我们从Clojure部分开始.Clojure从一开始就有一个clojure.core/read-string
函数,它read
是Read-Eval-Print-Loop的旧Lispy意义上的字符串,即它允许访问Clojure编译中使用的实际读者.[0]
后来,Rich Hickey&co决定推广Clojure的数据符号,并发布了EDN规范.EDN是Clojure的一个子集 ; 它仅限于Clojure语言的数据元素.
由于Clojure是一个Lisp,并且像所有lisps一样,吹嘘"代码是数据是代码"的哲学,上段的实际含义可能并不完全清楚.我不确定在任何地方都有详细的差异,但仔细检查Clojure Reader描述和前面提到的EDN规范会显示一些差异.最明显的区别在于宏字符,特别是#
调度符号,它在Clojure中比在EDN中有更多目标.例如,#(* % %)
符号是有效的Clojure,Clojure读者将变成以下EDN的等价物:(fn [x] (* x x))
.对于这个问题特别重要的是几乎没有记录的#=
特殊读取器宏,它可以用于在读者内部执行任意代码.
由于Clojure阅读器可以使用完整的语言,因此可以将代码嵌入到阅读器正在阅读的字符串中,然后在阅读器中对其进行评估.可以在这里找到一些例子.
该clojure.edn/read-string
功能严格限于EDN格式,而不是整个Clojure语言.特别是,它的操作不受*read-eval*
变量的影响,并且它无法读取所有可能的有效Clojure代码片段.
事实证明,由于历史原因,Clojure读者用Java编写.由于它是一个重要的软件,运行良好,并且经过大量的调试和经过多年积极的Clojure在野外使用的战斗测试,Rich Hickey决定在ClojureScript编译器中重用它(这是主要的原因) ClojureScript编译器在JVM上运行.ClojureScript编译过程主要发生在JVM上,其中Clojure读取器可用,因此ClojureScript代码由clojure.core/read-string
(或更确切地说是它的近亲clojure.core/read
)函数解析.
但是您的Web应用程序无法访问正在运行的JVM.要求ClojureScript应用程序的Java applet看起来并不是一个非常有前途的想法,特别是因为ClojureScript的主要目标是将Clojure语言的范围扩展到JVM(和CLR)的范围之外.因此决定ClojureScript无法访问自己的读者,因此也无法访问自己的编译器(即ClojureScript中没有eval
也read
没有read-string
).这个决定及其影响将在这里更详细地讨论,实际上知道事情是如何发生的(我不在那里,所以在这个解释的历史视角中可能存在一些不准确之处).
所以ClojureScript没有相应的clojure.core/read-string
(有些人认为它不是真正的口齿不清).仍然,有一些方法可以在Clojure服务器和ClojureScript客户端之间传递Clojure数据结构,这确实是EDN工作中的激励因素之一.正如Clojure 在EDN规范发布后获得了限制(和更安全)的阅读功能(clojure.edn/read-string
),ClojureScript也在标准发行版中获得了EDN阅读器cljs.reader/read-string
.可能有人认为,这两个函数的名称(或者更确切地说是它们的名称空间)之间的一致性会更好.
在我们最终回答你原来的问题之前,我们还需要一些关于这个问题的背景知识*print-dup*
.请记住,这*print-dup*
是Clojure 1.0的一部分,这意味着它早于EDN,标记文字的概念和记录.我认为EDN和标记文字为大多数用例提供了更好的选择*print-dup*
.由于Clojure通常建立在一些数据抽象(列表,向量,集合,映射和通常的标量)之上,因此打印/读取周期的默认行为是保留数据的抽象形状(地图是地图),但不是特别是它的具体类型.例如,Clojure的具有地图多个抽象实现方式中,如PersistentArrayMap用于小型地图和PersistentHashMap更大的一个.该语言的默认行为假定您不关心具体类型.
对于您所做的极少数情况,或者对于更专业的类型(当时使用deftype或defstruct定义),您可能希望更多地控制如何读取这些类型,这就是print-dup的用途.
关键是,*print-dup*
设置为true
,pr
并且系列不会产生有效的EDN,但实际上Clojure数据包括一些显式#=(eval build-my-special-type)
形式,这些形式都不是有效的EDN.
[0]:在"lisps"中,编译器是根据数据结构明确定义的,而不是根据字符串定义的.虽然这看起来与通常的编译器(在处理过程中确实将字符流转换为数据结构)有些不同,但Lisp的定义特征是读者发出的数据结构是常用的数据结构.语言.换句话说,编译器基本上只是该语言中始终可用的函数.这不像过去那样独特,因为大多数动态语言都支持某种形式eval
; Lisp的独特之处在于它eval
采用数据结构,而不是字符串,这使得动态代码生成和评估变得更加容易.编译器"只是另一个函数"的一个重要含义是编译器实际上运行时已经定义并可用的整个语言,并且到目前为止读取的所有代码也可用,这为Lisp宏系统打开了大门.