本文介绍了Clojure& ClojureScript:clojure.core / read-string,clojure.edn / read-string和cljs.reader / read-string的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 29岁程序员,3月因学历无情被辞! 我不清楚所有这些读字符串函数之间的关系。很明显, clojure.core / read-string 可以读取由 pr [n]输出的任何序列化字符串或甚至 print-dup 。还清楚的是, clojure.edn / read-string 会读取根据 EDN规范。 但是,我从Clojure脚本开始,并不清楚是否 cljs.reader / read-string 符合。这个问题是由于我有一个web服务正在以一种方式序列化的clojure代码: out-str(binding [* print-dup * true](prn tags))) 产生包括数据类型的对象序列化。但是, cljs.reader / read-string 不能读取。我总是得到这种类型的错误: 无法找到标记解析器为= in(instuuid js)格式应该是EDN(默认) 被 cljs-ajax 抛出,但在测试了一个rhino REPL中的 cljs.reader / read-string 同样的错误,这意味着它由 cljs.reader / read-string 本身抛出。它是由可能读标记类型函数在 cljs.reader 中引发的,但不清楚是否是因为读者只能使用EDN数据,或者如果...? 此外,从文档,唯一的说法是: read和read-string函数位于cljs.reader命名空间中 应该完全具有相同的行为。解决方案摘要: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 函数,其中读取 [0] 稍后,Rich Hickey(2004)指出, & co决定推广Clojure的数据符号并发布了 EDN规范。 EDN是Clojure的子集;它限于Clojure语言的数据元素。 由于Clojure是一个Lisp,像所有的lisps,toutscode is data is code哲学,上段的实际影响可能不是完全清楚。我不确定在任何地方有详细的差异,但仔细检查 Clojure读者描述和之前提到的EDN规范显示一些差异。最明显的区别在于宏字符,特别是#调度符号,在Clojure中有比EDN更多的目标。例如,#(*%%)符号是有效的Clojure,Clojure读取器将变成等效于以下EDN:(fn [x](* xx))。对这个问题特别重要的是几乎没有文档的#= 特殊阅读器宏,可以用来在阅读器内部执行任意代码。 由于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 (或者它的close cousin clojure.core / read )函数。 但是你的web应用程序不能访问正在运行的JVM。要求ClojureScript应用程序的Java小程序看起来不是一个非常有前途的想法,特别是因为ClojureScript的主要目的是扩展Clojure语言的范围超出JVM(和CLR)的限制。因此,决定ClojureScript将不能访问自己的读者,因此也不能访问自己的编译器(即没有 eval 也不是 read 或 read-string )。此决定及其影响将在这里更详细地讨论,由一个真正知道事情发生的人(我是因此ClojureScript没有等价的 clojure.core / read(),因为这个解释的历史观点可能有一些不准确。) (有人认为它不是一个真正的lisp)。但是,有一些方法可以在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是。 code> * print-dup * 设为 true , pr 将不会产生有效的EDN,但实际上Clojure数据包括一些明确的#=(eval build-my-special-type)有效的EDN。 [0]:在lisps中,编译器是根据数据结构而不是字符串来定义的。虽然这可能看起来像通常的编译器(这确实在将字符流转换为数据结构在处理过程中)的一个小差异,Lisp的定义特性是读者发出的数据结构是通常使用的数据结构语言。换句话说,编译器基本上只是一种在语言中随时可用的函数。这不像以前那么独特,因为大多数动态语言支持某种形式的 eval ; Lisp独有的是, eval 采用数据结构,而不是字符串,这使得动态代码生成和评估更容易。编译器的一个重要含义是只是另一个函数,即编译器实际上已经使用已定义和可用的整个语言运行,并且所有迄今为止所读取的代码都可用,这为Lisp宏系统打开了大门。 / p> I am not clear about the relationship between all these read-string functions. Well, it is clear that clojure.core/read-string can read any serialized string that is output by pr[n] or even print-dup. It is also clear that clojure.edn/read-string does read strings that are formatted according to the EDN specification.However, I am starting with Clojure Script, and it is not clear if cljs.reader/read-string comply with. This question has been triggered by the fact that I had a web service that was emiting clojure code serialized that way:(with-out-str (binding [*print-dup* true] (prn tags)))That was producing the object serialization which includes the datatypes. However, this was not readable by cljs.reader/read-string. I was always getting error of this type:Could not find tag parser for = in ("inst" "uuid" "queue" "js") Format should have been EDN (default)At first, I thought that this error was thrown by cljs-ajax but after testing the cljs.reader/read-string in a rhino REPL, I got the same error, which means it is thrown by cljs.reader/read-string itself. It is thrown by the maybe-read-tagged-type function in cljs.reader but it is not clear if this is because the reader only works with EDN data, or if...?Also, from the Differences from Clojure document, the only thing that is said is:The read and read-string functions are located in the cljs.reader namespaceWhich suggests that they should exactly have the same behavior. 解决方案 Summary: Clojure is a superset of EDN. By default, pr, prn and pr-str, when given Clojure data structures, produce valid EDN. *print-dup* changes that and makes them use the full power of Clojure to give stronger guarantees about the "sameness" of the objects in memory after a round-trip. ClojureScript can only read EDN, not full Clojure.Easy solution: do not set *print-dup* to true, and only pass pure data from Clojure to ClojureScript.Harder solution: use tagged literals, with a (possibly shared) associated reader on both sides. (This will still not involve *print-dup*, though.)Tangentially related: most use-cases for EDN are covered by Transit, which is faster, especially on the ClojureScript side.Let's start with the Clojure part. Clojure had, from the start, a clojure.core/read-string function, which reads a string in the old Lispy sense of the Read-Eval-Print-Loop, i.e. it gives access to the actual reader used in the compilation of Clojure.[0]Later on, Rich Hickey & co decided to promote the data notation of Clojure and published the EDN spec. EDN is a subset of Clojure; it is limited to the data elements of the Clojure language.As Clojure is a Lisp and, like all lisps, touts the "code is data is code" philosophy, the actual implications of the above paragraph may not be completely clear. I am not sure there is a detailed diff anywhere, but a careful examination of the Clojure Reader description and the previously mentioned EDN spec shows a few differences. The most obvious differences are around macro characters and in particular the # dispatch symbol, which has many more targets in Clojure than in EDN. For example, the #(* % %) notation is valid Clojure, which the Clojure reader will turn into the equivalent of the following EDN: (fn [x] (* x x)). Of particular importance for this question is the scarcely documented #= special reader macro, which can be used to execute arbitrary code right inside the reader.As the complete language is available to the Clojure reader, it is possible to embed code into the character string that the reader is reading and have it evaluated right then and there in the reader. A few examples can be found here.The clojure.edn/read-string function is strictly limited to the EDN format, not the whole Clojure language. In particular, its operation is not influenced by the *read-eval* variable and it cannot read all of the valid Clojure code fragments possible.It turns out that the Clojure reader is, for mostly historical reasons, written in Java. As it is a significant piece of software, works well, and has been largely debugged and battle-tested by a few years of active Clojure usage in the wild, Rich Hickey decided to reuse it in the ClojureScript compiler (this is the main reason why the ClojureScript compiler runs on the JVM). The ClojureScript compilation process happens mostly on the JVM, where the Clojure reader is available, and thus ClojureScript code is parsed by the clojure.core/read-string (or rather its close cousin clojure.core/read) function.But your web application does not have access to a running JVM. Requiring a Java applet for ClojureScript applications did not look like a very promising idea, especially as the main objective of ClojureScript was to extend the reach of the Clojure language beyond the confines of the JVM (and the CLR). So the decision was taken that ClojureScript would not have access to its own reader, and consequently would not have access to its own compiler either (i.e. there is no eval nor read nor read-string in ClojureScript). This decision and its implications are discussed in greater details here, by someone who actually knows how things happened (I was not there, so there may be some inaccuracies in the historical perspective of this explanation).So ClojureScript has no equivalent of clojure.core/read-string (and some would argue that it is therefore not a true lisp). Still, it would be nice to have some way to communicate Clojure data structures between a Clojure server and a ClojureScript client, and indeed that was one of the motivating factors in the EDN effort. Just as Clojure got a restricted (and safer) reading function (clojure.edn/read-string) after the publication of the EDN spec, ClojureScript also got an EDN reader in the standard distribution as cljs.reader/read-string. It may be argued that a little more consistency between the names of these two functions (or rather their namespace) would have been good.Before we can finally answer your original question, we need one more little piece of context regarding *print-dup*. Remember that *print-dup* was part of Clojure 1.0, which means it predates EDN, the notion of tagged literals, and records. I would argue that EDN and tagged literals offer a better alternative for most of the use-cases of *print-dup*. As Clojure is generally built on top of a few data abstractions (list, vector, set, map, and the usual scalars), the default behaviour of the print/read cycle is to preserve the abstract shape of the data (a map is a map), but not especially its concrete type. For example, Clojure has multiple implementations of the map abstraction, such as PersistentArrayMap for small maps and PersistentHashMap for bigger one. The default behaviour of the language assumes that you do not care about the concrete type.For the rare cases where you do, or for the more specialized types (defined with deftype or defstruct, at the time), you might want more control about how these are read, and that is what print-dup is for.The point is, with *print-dup* set to true, pr and family will not produce valid EDN, but actually Clojure data including some explicit #=(eval build-my-special-type) forms, which are not valid EDN.[0]: In "lisps", the compiler is explicitly defined in terms of data structures, rather than in terms of character strings. While that may seem like a small difference with usual compilers (which do indeed transform the character stream into data structures during their processing), the defining characteristic of Lisp is that the data structures that are emitted by the reader are the data structures commonly used in the language. In other words, the compiler is basically just a function available at all times in the language. This is not as unique as it used to be, as most dynamic languages support some form of eval; what is unique to Lisp is that eval takes a data structure, not a character string, which makes dynamic code generation and evaluation much easier. One important implication of the compiler being "just another function" is that the compiler actually runs with the whole language already defined and available, and all of the code read so far also available, which opens up the door to the Lisp macro system. 这篇关于Clojure& ClojureScript:clojure.core / read-string,clojure.edn / read-string和cljs.reader / read-string的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云!
07-21 02:15
查看更多