簡體   English   中英

Clojure&ClojureScript:clojure.core / read-string,clojure.edn / read-string和cljs.reader / read-string

[英]Clojure & ClojureScript: clojure.core/read-string, clojure.edn/read-string and cljs.reader/read-string

我不清楚所有這些讀字符串函數之間的關系。 好吧,很明顯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引發的,但是在rhino REPL中測試cljs.reader/read-string之后,我得到了同樣的錯誤,這意味着它被cljs.reader/read-string本身拋出。 它是由cljs.readermaybe-read-tagged-type函數cljs.reader但不清楚這是因為讀者只能使用EDN數據,還是......?

另外,根據與Clojure文檔的差異 ,唯一可以說的是:

The read and read-string functions are located in the cljs.reader namespace

這表明他們應該完全具有相同的行為。

簡介:Clojure是EDN的超集。 默認情況下, prprnpr-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意義上的sa字符串,即它可以訪問Clojure編譯中使用的實際讀者。[0]

后來,Rich Hickey&co決定推廣Clojure的數據符號,並發布了EDN規范 EDN是Clojure的一個子集 ; 它僅限於Clojure語言的數據元素。

由於Clojure是一個Lisp,並且像所有lisps一樣,吹噓“代碼是數據是代碼”的哲學,上段的實際含義可能並不完全清楚。 我不確定在任何地方都有詳細的差異,但仔細檢查Clojure Reader描述和前面提到的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 (或者更接近它的近似表兄弟clojure.core/read )函數解析。

但是您的Web應用程序無法訪問正在運行的JVM。 要求ClojureScript應用程序的Java applet看起來並不是一個非常有前途的想法,特別是因為ClojureScript的主要目標是將Clojure語言的范圍擴展到JVM(和CLR)的范圍之外。 因此,決定ClojureScript無法訪問自己的閱讀器,因此也無法訪問自己的編譯器(即ClojureScript中沒有eval也沒有readread-string )。 這個決定及其影響將在這里更詳細地討論,實際上知道事情是如何發生的(我不在那里,所以在這個解釋的歷史視角中可能存在一些不准確之處)。

所以ClojureScript沒有相當於clojure.core/read-string (有些人認為它不是真正的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的用途。

關鍵是,如果*print-dup*設置為truepr和family將不會產生有效的EDN,但實際上Clojure數據包括一些顯式的#=(eval build-my-special-type)形式,這些形式不是有效的EDN。

[0]:在“lisps”中,編譯器是根據數據結構明確定義的,而不是根據字符串定義的。 雖然這看起來與通常的編譯器(在處理過程中確實將字符流轉換為數據結構)有些不同,但Lisp的定義特征是讀者發出的數據結構是常用的數據結構。語言。 換句話說,編譯器基本上只是該語言中始終可用的函數。 這不像過去那樣獨特,因為大多數動態語言都支持某種形式的eval ; Lisp的獨特之處在於eval采用的是數據結構,而不是字符串,這使得動態代碼生成和評估變得更加容易。 編譯器“只是另一個函數”的一個重要含義是編譯器實際上運行時已經定義並可用的整個語言,並且到目前為止讀取的所有代碼也可用,這為Lisp宏系統打開了大門。

cljs.reader/read僅支持EDN,但pr等將輸出無法讀取的標簽(特別是用於協議和記錄)。

一般來說,如果在Clojure方面你可以驗證(= value (clojure.edn/read-string (pr-str value))) ,你的cljs interop應該可以工作。 這可能是限制性的,並且對EDN庫的變通方法或修復程序進行了一些討論。

根據您的數據的樣子,您可以查看Clojure Cookbook中描述的tagged庫。

實際上,可以通過cljs.reader / register-tag-parser注冊自定義標簽解析器!

對於記錄我看起來像這樣:( (register-tag-parser! (s/replace (pr-str m/M1) "/" ".") m/map->M1)

@Gary - 相當不錯的答案

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM