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


Tüm bu read-string fonksiyonları arasındaki ilişki hakkında net değilim. Şey, belli ki clojure.core/read-string tarafından çıkarılan herhangi bir serileştirilmiş dizeyi okuyabilir pr[n] ya da print-dup. Ayrıca açıktır ki clojure.edn/read-string göre biçimlendirilmiş dizeleri okur EDN belirtimi.

Ancak, Clojure Script ile başlıyorum ve eğer açık değil cljs.reader/read-string uymak. Bu soru, şu şekilde serileştirilmiş bir clojure kodu yayan bir web servisine sahip olmamızla tetiklendi:

(with-out-str (binding [*print-dup* true] (prn tags)))

Bu, veri tiplerini içeren nesne serileştirmesi üretiyordu. Ancak, bu okunabilir değil cljs.reader/read-string. Her zaman bu tip bir hatayı alıyorum:

Could not find tag parser for = in ("inst" "uuid" "queue" "js")  Format should have been EDN (default)

İlk başlarda, bu hatanın atıldığını düşündüm. cljs-ajax ama test ettikten sonra cljs.reader/read-string bir rhino REPL içinde, aynı hatayı aldım, yani cljs.reader/read-string kendisi. Tarafından atılır maybe-read-tagged-type işlevi cljs.reader ancak bunun, okuyucu sadece EDN verisi ile çalıştığı ya da ...?

Ayrıca, Clojure arasındaki farklar Belge, söylenen tek şey:

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

Hangisi aynı davranışa sahip olmaları gerektiğini gösteriyor.


25
2017-07-09 18:53


Menşei




Cevaplar:


Özet: Clojure, EDN'nin bir üst kümesidir. Varsayılan olarak, pr, prn ve pr-strClojure veri yapıları verildiğinde geçerli EDN üretir. *print-dup* değişiklikleri değiştirir ve onları bir gidiş-dönüşten sonra bellekteki nesnelerin "aynılığı" hakkında daha güçlü garantiler vermek için Clojure'ın tam gücünü kullanır. ClojureScript sadece, tam Clojure değil EDN okuyabilir.

Kolay çözüm: ayarlamayın *print-dup* true ve sadece Clojure'dan ClojureScript'e giden saf verileri geçirir.

Daha zor bir çözüm: Her iki tarafta bir (muhtemelen paylaşılmış) ilgili okuyucu ile etiketlenmiş hazır sözcükleri kullanın. (Bu hala içermeyecek *print-dup*olsa da.)

Tanjantla ilgili: EDN için çoğu kullanım vakası TransitÖzellikle ClojureScript tarafında daha hızlıdır.


Clojure bölümü ile başlayalım. Clojure, başından beri bir clojure.core/read-string işlevi, hangi readRead-Eval-Print-Loop'un eski Lispy anlamında bir dizgedir, yani Clojure derlemesinde kullanılan gerçek okuyucuya erişim sağlar.

Daha sonra, Rich Hickey & co, Clojure'ın veri gösterimini teşvik etmeye karar verdi ve EDN özellikleri. EDN bir alt küme Clojure; Clojure dilinin veri öğeleriyle sınırlıdır.

Clojure bir Lisp olduğundan ve tüm lisps gibi, "kodun veri koddur" felsefesine ses çıkarır, yukarıdaki paragrafın asıl sonuçları tamamen açık olmayabilir. Her yerde detaylı bir farklılık olduğundan emin değilim, ama dikkatli bir inceleme Clojure Okuyucu açıklaması ve daha önce belirtilen EDN spesifikasyonları birkaç farklılık göstermektedir. En belirgin farklılıklar makro karakterler ve özellikle de #Clojure'da EDN'den çok daha fazla hedefi olan gönderim sembolü. Örneğin, #(* % %) notasyon, Clojure okuyucunun aşağıdaki EDN'ye karşılık geleceği geçerli bir Clojure'dir: (fn [x] (* x x)). Bu soru için özellikle önemli olan belgelendirilmiş #= Okuyucunun içinde rastgele kod yürütmek için kullanılabilecek özel okuyucu makrosu.

Tam dil, Clojure okuyucusu için kullanılabilir olduğu için, okuyucunun okuduğu karakter dizisine kod yerleştirmek ve okuyucunun o anda ve oradan değerlendirilmesini sağlamak mümkündür. Birkaç örnek bulunabilir İşte.

clojure.edn/read-string işlevi tamamen Clojure dili değil, EDN formatıyla sınırlıdır. Özellikle, çalışması etkilenmez. *read-eval* değişken ve geçerli tüm Clojure kod parçalarını okuyamaz.

Clojure okuyucusunun, çoğunlukla tarihsel nedenlerden dolayı Java'da yazılmış olduğu ortaya çıkıyor. Önemli bir yazılım parçası olduğu için, iyi çalışıyor ve büyük ölçüde hata ayıklandı ve vahşi bir şekilde aktif Clojure kullanımı birkaç yıl boyunca savaş-test edildi, Rich Hickey ClojureScript derleyicide yeniden kullanmaya karar verdi (Bu ana nedeni nedir ClojureScript derleyicisi JVM'de çalışır. ClojureScript derleme işlemi çoğunlukla Clojure okuyucusunun bulunduğu JVM'de olur ve bu nedenle ClojureScript kodu tarafından ayrıştırılır. clojure.core/read-string (ya da onun yakın kuzeni clojure.core/read) işlevi.

Ancak web uygulamanızın çalışan bir JVM'ye erişimi yok. ClojureScript uygulamaları için bir Java uygulaması gerektirmesi çok umut verici bir fikir gibi görünmüyordu; özellikle de ClojureScript'in temel amacı, Clojure dilinin erişimini JVM'nin (ve CLR'nin) sınırlarının ötesine uzatmaktı. Dolayısıyla ClojureScript'in kendi okuyucusuna erişemeyeceği ve dolayısıyla kendi derleyicisine erişiminin olmayacağı kararı alındı ​​(yani, eval ne de read ne de read-string ClojureScript'te). Bu karar ve etkileri daha ayrıntılı olarak tartışılmaktadır. İşteaslında şeylerin nasıl olduğunu bilen biri tarafından (orada değildim, bu nedenle bu açıklamanın tarihsel perspektifinde bazı yanlışlıklar olabilir).

Yani ClojureScript'in bir karşılığı yok clojure.core/read-string (ve bazıları bunun gerçek bir lisp olmadığını iddia eder). Yine de, Clojure veri yapılarını bir Clojure sunucusu ile bir ClojureScript istemcisi arasında iletişim kurmanın bir yolu olması güzel olurdu ve aslında bu EDN çabasındaki motive edici faktörlerden biriydi. Sadece Clojure kısıtlanmış (ve daha güvenliokuma fonksiyonuclojure.edn/read-string) EDN spesifikasyonunun yayınlanmasından sonra, ClojureScript standart dağıtımda EDN okuyucusunu da aldı cljs.reader/read-string. Bu iki fonksiyonun isimleri arasında biraz daha fazla tutarlılık (veya daha ziyade ad alanları) iyi olurdu.

Nihayet orijinal sorunuzu cevaplayabilmemiz için, konuyla ilgili bir parça daha küçük bir konuya ihtiyacımız var. *print-dup*. Bunu hatırla *print-dup* Clojure 1.0'ın bir parçasıydı, bu da EDN, etiketlenmiş değişmezler kavramı ve kayıtlardan önce gelir. EDN ve etiketlenmiş edebi eserlerin çoğu için daha iyi bir seçenek sunduğunu iddia ediyorum *print-dup*. Clojure genellikle birkaç veri soyutlamasının (liste, vektör, küme, harita ve her zamanki skaler) üzerine kurulduğundan, baskı / okuma döngüsünün varsayılan davranışı, verinin soyut şeklini korumaktır. harita), ancak özellikle beton türü değil. Örneğin Clojure, harita soyutlamasının çoklu uygulamalarına sahiptir. PersistentArrayMap küçük haritalar ve PersistentHashMap daha büyük olan için. Dilin varsayılan davranışı, beton türünü umursamadığınızı varsayar.

Yaptığınız nadir durumlarda ya da daha özel tipler için (zamanla deftype ya da defstruct ile tanımlanmış), bunların nasıl okunduğuna dair daha fazla kontrol isteyebilirsiniz, bu da print-dup'in amacıdır.

Nokta, ile *print-dup* ayarlanır true, pr ve aile geçerli bir EDN üretmeyecek, ancak bazı açık bilgileri içeren Clojure verileri #=(eval build-my-special-type) formları değil geçerli EDN.

[0]: "Lisps" de derleyici, karakter dizileri açısından değil, veri yapıları açısından açıkça tanımlanır. Bu, normal derleyicilerle küçük bir fark gibi görünse de (aslında karakter akışını, işlem sırasında veri yapılarına dönüştüren), Lisp'in tanımlama özelliği, okuyucu tarafından yayılan veri yapılarının, yaygın olarak kullanılan veri yapıları olmasıdır. dil. Başka bir deyişle, derleyici temel olarak sadece dilde her zaman mevcut olan bir işlevdir. Çoğu dinamik dilin bazı biçimlerini desteklediği için bu, eskiden olduğu kadar benzersiz değildir. eval; Lisp'e özgü olan, eval dinamik kod üretimi ve değerlendirmesini çok daha kolay hale getiren bir karakter dizisini değil, veri yapısını alır. Derleyicinin "sadece başka bir işlev" olmasının önemli bir anlamı, derleyicinin gerçekten zaten tanımlanmış ve kullanılabilir olan tüm dil ile çalıştığı ve Lisp makro sistemine giden kapıyı açan, şu ana kadar okunan tüm kodların çalıştığıdır.


44
2017-09-07 16:59



Yazdığın için teşekkürler. Çok bilgilendirici. - Dominykas Mostauskis


cljs.reader/read sadece EDN'yi destekler, ancak pr vb okunmayacak etiketler (özellikle protokoller ve kayıtlar için) verecektir.

Genel olarak, Clojure tarafındaysa bunu doğrulayabilirsiniz. (= value (clojure.edn/read-string (pr-str value)))senin cljs interop çalışmalıdır. Bu, sınırlayıcı olabilir ve EDN kitaplığına geçici çözümler veya düzeltmeler hakkında bazı tartışmalar vardır.

Verilerinizin neye benzediğine bağlı olarak, tagged açıklandığı gibi kütüphane Clojure Yemek Tarifleri.


4
2017-07-09 22:32



@ micheal-victor-zink Benim varsayımımı doğruladığın için teşekkürler! (bu cljs.reader/read[-read]EDN ile uyumludur. Ancak, bu gerçek hakkında herhangi bir belge var mı. Bu koda bakıyorum ve bunun aslında böyle olmadığını ve ne yazıldığını açıklamıyorum. - Neoasimov
Cljs.reader / read ve cljs.reader / read-string arasındaki fark nedir? - johnbakers
@hellofunk read bir alır PushbackReader, süre read-string yapar PushbackReader dize dışında read üstünde. - Michael Victor Zink


Aslında, cljs.reader / register-tag-parser aracılığıyla özel etiket ayrıştırıcısını kaydetmek mümkün!

bir kayıt için böyle görünüyor: (register-tag-parser! (s/replace (pr-str m/M1) "/" ".") m/map->M1)

@Gary - oldukça iyi cevap


4
2018-03-18 21:47