- Clojure 1.12.0 Java 8 bytecode को बनाए रखता है, और इसे बाद की releases में न्यूनतम Java compatibility और bytecode baseline को नए Java LTS पर ले जाने से पहले Java 8 baseline वाली आखिरी release के रूप में संकेतित किया गया है
- JDK 21 के virtual threads environment में
lazy-seqऔरdelay,synchronizedकी जगह lock का उपयोग करते हैं, जिससे blocking I/O के real thread को pin करने की स्थितियां कम होती हैं - REPL में JVM restart किए बिना
add-lib,add-libs,sync-depsसे libraries जोड़ी जा सकती हैं, लेकिन यह सुविधा सिर्फ development के दौरान interactive उपयोग तक सीमित है - Java interoperability बढ़ी है: method values,
:param-tags, array class syntax, functional interface conversion,Supplier, और JavaStreamprocessing functions जोड़े गए हैं - performance और compatibility के लिहाज से
PersistentVectorspliterator, efficientdrop/partition processing, Var interning policy को सख्त करना, CVE-2024-22871 fix, और Java serialization identifiers की सफाई शामिल है
Java 8 compatibility, security, serialization cleanup
- Clojure 1.12.0 download और उपयोग की जानकारी Downloads page पर मिल सकती है
- Java 8 baseline इस release में भी कायम है
- Clojure 1.12, Clojure 1.10 और 1.11 की तरह Java 8 bytecode generate करता है
- बाद की releases bytecode और minimum Java compatibility को ज्यादा नए Java LTS release पर ले जाएंगी
- JDK 21 की virtual thread pinning समस्या कम की गई है
- 1.12 से पहले,
lazy-seqऔरdelayएक बार ही execute होने वाले behavior की guarantee देने के लिए user code कोsynchronizedblock के अंदर चलाते थे - JDK 21 के मानक के अनुसार
synchronizedअभी cooperative blocking में हिस्सा नहीं लेता, इसलिए अगर वह code blocking I/O करता है तो real thread को pin कर सकता है -Djdk.tracePinnedThreads=fullइस्तेमाल करने पर JDK 21 इस स्थिति के बारे में warning दे सकता है- 1.12 में
lazy-seqऔरdelay,synchronizedblock की जगह lock का उपयोग करते हैं
- 1.12 से पहले,
- security fix के रूप में CVE-2024-22871 शामिल किया गया है, और संबंधित advisory GHSA-vr64-r9qj-h27f पर है
- Java serialization से जुड़ी classes का serialVersionUID explicit रूप से set किया गया है
- Clojure data types ने Clojure 1.0 से Java serialization interface implement किया है
- Java serialization तभी काम करता है जब class name, type hierarchy और serialization fields के आधार पर बनने वाला identifier deserialization के समय match करे
- Clojure versions के बीच serialization consistency की guarantee नहीं देता, लेकिन जरूरत से ज्यादा compatibility न टूटे, इसके लिए आगे control बढ़ाने वाला बदलाव लागू किया गया है
- dependencies भी साथ में update की गई हैं
spec.alphaको 0.5.238 पर update किया गया हैcore.specs.alphaको 0.4.74 पर update किया गया है
REPL में libraries और tools संभालने की सुविधाएं
- development के दौरान कभी-कभी JVM restart किए बिना libraries जोड़ने की जरूरत होती है
- experimental evaluation
- project में known dependency जोड़ना
- किसी खास task के लिए library जोड़ना
- Clojure 1.12 REPL state खोए बिना libraries जोड़ने के लिए नए functions देता है
add-lib: classpath में न मौजूद lib को download कर classloader में जोड़ता है- जो lib पहले से classpath में है उसे update नहीं करता
- coordinates न हों तो latest Maven version, या git repository name infer किया जा सके तो latest git version या tag का उपयोग करता है
add-libs: कई नई libraries और versions को साथ में resolve करता हैsync-deps:deps.ednमें मौजूद लेकिन अभी classpath में न होने वाले libs के लिएadd-libscall करता है
- ये functions सिर्फ development के दौरान REPL उपयोग के लिए intended हैं
- production code को build और maintain करने का सही तरीका अब भी
deps.ednका उपयोग है - तीनों functions check करते हैं कि
*repl*true से bound है या नहीं clojure.main/replइस flag को automatic रूप से bind करता हैclojure.mainREPL में नए functionsusernamespace में automatically refer हो जाते हैं- दूसरे REPLs में
(require '[clojure.repl.deps :refer :all])की जरूरत पड़ सकती है
- production code को build और maintain करने का सही तरीका अब भी
- library resolution और download tools.deps संभालता है
- development के दौरान project classpath में
tools.depsऔर उसकी dependencies डालने से बचाने के लिए, Clojure CLI के जरिए functions को अलग process में call करने वाला नया API भी जोड़ा गया है
- development के दौरान project classpath में
clojure.tools.deps.interop/invoke-tooltool function को अलग process में call करता है- tool का classpath
deps.ednमें define होता है - tool dependencies को project classpath में जोड़ने की जरूरत नहीं होती
add-libfeatureinvoke-toolका उपयोग करके बनाया गया है, और इसे user tools को interactively build या call करने में भी इस्तेमाल किया जा सकता है- function execution protocol CLI reference में देखा जा सकता है
- tool का classpath
External process execution API
- मौजूदा
clojure.java.shellnamespace के अलावा, Java के नए process information, control और I/O redirection APIs का उपयोग करने वाला रास्ता बना है - Clojure 1.12 नया namespace
clojure.java.processजोड़ता है- Java के नए process-related APIs का उपयोग करता है
- मौजूदा तरीके की तुलना में इसे इस्तेमाल में आसान बनाने के लिए design किया गया है
- मुख्य functions ये हैं
Java interoperability expansion
- method values जोड़ी गई हैं, जिससे Java methods को higher-order functions में ज्यादा सीधे इस्तेमाल किया जा सकता है
- पहले Java methods को
mapआदि में pass करने के लिए manually function में wrap करना पड़ता था - manual wrapping verbose था, overload अलग करने के लिए hints की जरूरत पड़ सकती थी, या extra reflection/boxing हो सकती थी
- अब qualified methods को value position में normal functions की तरह इस्तेमाल किया जा सकता है, और compiler wrapping function automatically generate करता है
- अगर qualified method overload की वजह से resolve नहीं होता, तो compiler reflection call generate करता है
- developers
:param-tagsmetadata से desired single method signature specify कर सकते हैं
- पहले Java methods को
- Qualified method syntax class और method को explicit करता है
Classname/method: static method call करने वाली Clojure function valueClassname/.method: instance method call करने वाली Clojure function valueClassname/new: constructor call करने वाली Clojure function value- static method और instance method में फर्क करने के लिए
Classname/methodऔरClassname/.methodsyntax का उपयोग करना होगा
:param-tagsmetadata overloaded methods resolve करने में इस्तेमाल होता है- value के रूप में इस्तेमाल होने वाला qualified method सिर्फ class और method name देता है, इसलिए overloaded method resolve नहीं कर सकता
:param-tags[tag …]form का vector है, और हर tag desired signature के parameter से correspond करता है- non-overloaded type के parameters के लिए
_placeholder इस्तेमाल किया जा सकता है :param-tagsदेने पर compiler को compile time पर single method में resolve कर पाना चाहिए- नया metadata reader syntax
^[tag …]member symbol पर:param-tagsmetadata जोड़ता है
- Array class syntax जोड़ा गया है
- Clojure class name symbols को class object values और type hints के रूप में support करता था, लेकिन strings के अलावा array class syntax नहीं देता था
- अब
ComponentClass/#dimensionsform के symbol से array class refer की जा सकती है - उदाहरण:
String/1,java.lang.String/1,long/2 - component class fully qualified class name, imported class, या primitive हो सकती है
- array class syntax type hint और value, दोनों रूपों में इस्तेमाल किया जा सकता है
- Java functional interface interoperability बेहतर हुई है
- Java functional interface पर
@FunctionalInterfaceलगा होता है और उसमें single method होता है - Clojure function, arity match होने पर functional interface लेने वाले Java method call में pass किया जा सकता है
- Clojure compiler lambda adapter बनाकर Clojure function को required functional interface में implicitly convert करता है
- loop में बार-बार adapter creation से बचने के लिए
letbinding name पर hint लगाकर explicitly force किया जा सकता है
- Java functional interface पर
Supplierinteroperability भी बेहतर हुई है- value supply करने वाला
Supplierलेने वाले methods को call करने के लिए पहलेreifyसे adapter लिखना पड़ता था - Clojure के
IDerefimplementers जैसेdelay,future,atomआदि अब सीधेSupplierinterface implement करते हैं
- value supply करने वाला
Stream processing और collections performance सुधार
- Java APIs अब लगातार ज्यादा
Streamreturn करते हैं; उन्हें Clojure तरीके से consume करने के लिए functions जोड़े गए हैं- Clojure 1.12 के functional interface support के साथ Stream interoperability functions दिए गए हैं
(stream-seq! stream) ⇒ seq(stream-reduce! f [init-val] stream) ⇒ val(stream-transduce! xf f [init-val] stream) ⇒ val(stream-into! to-coll [xf] stream) ⇒ to-coll- सभी functions terminal stream operations हैं और stream को consume करते हैं
PersistentVectorJava collections के stream implementation में इस्तेमाल होने वाला spliterator खुद provide करता है- spliterator ऐसा iterator है जिसे तेज parallel traversal के लिए split किया जा सकता है
PersistentVectorका नया custom spliterator parallelism support करता है और performance में बड़ा सुधार करता है
drop,nthrest,nthnextऔर partition processing की efficiency बेहतर हुई है- CLJ-2713 ने internal interface
IDropजोड़ा है, जो बताता है कि collection sequential traversal की तुलना में ज्यादा efficiently drop कर सकती है - यह interface persistent collections और
range,repeatजैसी algorithmic collections में implement किया गया है - नए functions
partitionv,partitionv-all,splitv-atमौजूदा counterparts से ज्यादा efficient हैं, और realized seq partitions की जगह vector partitions बनाते हैं
- CLJ-2713 ने internal interface
Var interning policy सख्त
- namespace में var को intern करना aliasing से अलग है; इसका मतलब stable reference बनाना है ताकि सभी references को same object मिले
- पहले कुछ cases में interned var replace हो सकता था, और 1.12.0-alpha1 में policy ज्यादा strict हुई
- ऐसी स्थिति होने पर
"REJECTED: attempt to replace interned var #'some-ns/foo with #'other-ns/foo in some-ns, you must ns-unmap first"जैसी warning दिखती है
- ऐसी स्थिति होने पर
- यह policy Clojure 1.11.0 में
clojure.coreमें नए functions, खासकरabs, जुड़ने से सामने आई problem के root cause को address करती है
पूरा बदलावों की सूची
- Clojure 1.12.0 की पूरी बदलावों की सूची official changelog में है
1 टिप्पणियां
Hacker News की राय
यह सचमुच एक बड़ा रिलीज़ है और इसमें बहुत-सी शानदार नई features हैं
व्यक्तिगत रूप से मुझे add-libs सबसे ज़्यादा पसंद है। अब single-file demo या issue reproduce करने के लिए minimal example बनाया जा सकता है, इसलिए चलने योग्य छोटे code snippets साझा करने की बाधा काफी कम हो जाती है
Java boilerplate के बिना Java libraries का demo भी किया जा सकता है। REPL में थोड़ा प्रयोग करने के बाद अगर कोड को HN comment जैसी किसी जगह paste कर दें, तो कोई भी उसी “setup” को वैसा का वैसा reproduce करके चला सकता है। Repository clone करने की भी ज़रूरत नहीं है
मुझे लगा था कि इस रिलीज़ को Clojure/conj 2024 तक टाल दिया जाएगा। कोई खास आधार नहीं था, लेकिन Clojure 1.10 लगभग Clojure/conj 2021 के समय आया था और Datomic को free करने की घोषणा भी Clojure/conj 2023 की शुरुआत में हुई थी
फिर भी मैं अभी spec2 का इंतज़ार कर रहा हूँ। अभी spec की rigidness को Malli से bypass कर लेते हैं, लेकिन वह Clojure में first-class citizen नहीं है। मुख्य वजह यह है कि macros को inspect नहीं किया जा सकता, और यह Clojure compiler design का एक जानबूझकर रखा गया हिस्सा है। हाँ, Malli schema को data की तरह manipulate करके schema/select के विचार की नकल की जा सकती है
functional interface बदलाव की वजह से अब
(defmacro ->Consumer [f] ...)जैसे utility macro को बनाए रखने की ज़रूरत नहीं रही; अब function को सीधे pass किया जा सकता हैवास्तव में Clojure core भी spec से macro calls की जाँच करता है, इसलिए गलत call होने पर कभी-कभी stack trace में उसका निशान दिखता है। जानना चाहता हूँ कि आपका मतलब कुछ और था क्या
यह बहुत अच्छा है कि इतनी सारी नई features आने के बावजूद पुराना code वैसे ही चलता है। breaking changes से बचने की लगातार कोशिश साफ दिखती है
अगर आप Clojure के बारे में और जानना चाहते हैं, तो 23–25 अक्टूबर को Alexandria, Virginia में होने वाले Clojure/conj conference पर नज़र डाल सकते हैं: https://2024.clojure-conj.org
add-libs और sync-deps का आना अच्छा लगा। अब session को ज़बरदस्ती बंद करने की वजह लगभग नहीं बची, या शायद बिल्कुल भी नहीं
यह रिलीज़ scope के लिहाज़ से पिछली releases से काफी अलग लगती है, और इसमें बहुत कुछ जोड़ा गया है, इसलिए यह दिलचस्प है। बस उम्मीद है कि तेज़ रफ़्तार की वजह से कुछ releases बाद यह उलझा हुआ ढेर न बन जाए
functional interface बदलाव बहुत बड़ा है। Clojure तब सबसे अच्छा लगता है जब वह सोच-समझकर की गई interop के साथ Java के करीब रहता है, और इस बदलाव ने एक बड़ी कमी को भर दिया है
सोच रहा हूँ spec का क्या हुआ। क्या उसे छोड़ दिया गया है? कोई उत्साहजनक खबर है क्या
यह एक काफी ठोस release लगती है, और यह देखकर खुशी होती है कि Clojure अभी भी अच्छा कर रहा है
मैं trolling नहीं कर रहा। मैं इसे चुनना चाहता हूँ, और engineering के नज़रिए से यह अच्छा निर्णय लगता है। लेकिन अगर popularity और contributors तेज़ी से गिर रहे हैं, तो निकट भविष्य में यह रुकावट बन सकता है
[0] https://www.clara-rules.org/
मौजूदा developers को Clojure developers में बदलना अब पहले से कहीं आसान हो गया है
शुरुआत में आने वाली बड़ी समस्या[0] code पढ़ना होती है, और ChatGPT या Claude जैसे AI मौजूदा Clojure code को समझाने में बहुत अच्छे हैं। नतीजतन developer onboarding काफ़ी तेज़ हो सकता है
[0] कुछ हफ़्तों बाद Clojure पढ़ना स्वाभाविक लगने लगता है, और आप यह भी भूल जाते हैं कि पहले आप इसे पढ़ नहीं पाते थे
बहुत-से शानदार improvements हैं। यह आम तौर पर वह Lisp family language है जिसकी ओर मेरा हाथ सबसे पहले बढ़ता है