2 puan yazan GN⁺ 2024-09-06 | 1 yorum | WhatsApp'ta paylaş
  • Clojure 1.12.0, Java 8 bytecode’unu korurken, sonraki sürümlerde minimum Java uyumluluğu ve bytecode tabanını daha yeni bir Java LTS sürümüne taşımadan önceki son Java 8 tabanlı sürüm olarak duyuruldu
  • JDK 21’in sanal thread ortamında lazy-seq ve delay, synchronized yerine lock kullanarak, blocking I/O’nun gerçek thread’leri pinlediği durumları azaltıyor
  • REPL’de JVM’i yeniden başlatmadan add-lib, add-libs, sync-deps ile kütüphane eklenebiliyor; ancak bu özellik yalnızca geliştirme sırasında etkileşimli kullanım için tasarlandı
  • Java birlikte çalışabilirliği genişletilerek method values, :param-tags, dizi sınıfı sözdizimi, fonksiyonel arayüz dönüşümü, Supplier, Java Stream işleme fonksiyonları eklendi
  • Performans ve uyumluluk tarafında PersistentVector spliterator, daha verimli drop/partition işleme, daha sıkı Var interning politikası, CVE-2024-22871 düzeltmesi ve Java serileştirme tanımlayıcılarının düzenlenmesi yer alıyor

Java 8 uyumluluğu, güvenlik ve serileştirme düzenlemeleri

  • Clojure 1.12.0 indirme ve kullanım bilgileri Downloads sayfasında bulunabilir
  • Java 8 tabanı bu sürümde de korunuyor
    • Clojure 1.12, Clojure 1.10 ve 1.11 gibi Java 8 bytecode’u üretiyor
    • Sonraki sürümler, bytecode ve minimum Java uyumluluğunu daha yeni bir Java LTS sürümüne taşımayı planlıyor
  • JDK 21’deki sanal thread pinning sorunu hafifletildi
    • 1.12’den önce lazy-seq ve delay, yalnızca bir kez çalıştırılma davranışını garanti etmek için kullanıcı kodunu synchronized blok içinde yürütüyordu
    • JDK 21 itibarıyla synchronized hâlâ cooperative blocking’e katılmadığından, ilgili kod blocking I/O yaparsa gerçek thread’i pinleyebiliyor
    • -Djdk.tracePinnedThreads=full kullanıldığında JDK 21 bu durum için uyarı verebiliyor
    • 1.12’de lazy-seq ve delay, synchronized blok yerine lock kullanıyor
  • Güvenlik düzeltmesi olarak CVE-2024-22871 eklendi; ilgili uyarı GHSA-vr64-r9qj-h27f adresinde yer alıyor
  • Java serileştirmeyle ilgili sınıfların serialVersionUID değeri açıkça ayarlandı
    • Clojure veri tipleri, Clojure 1.0’dan beri Java serileştirme arayüzünü uyguluyor
    • Java serileştirme, sınıf adı, tip hiyerarşisi ve serileştirme alanlarından üretilen tanımlayıcının deserialization sırasında eşleşmesini gerektiriyor
    • Clojure sürümler arası serileştirme tutarlılığı garantisi vermese de, gelecekte gereksiz uyumluluk kırılmalarını önlemek için daha fazla kontrol sağlayan bir değişiklik yaptı
  • Bağımlılıklar da güncellendi
    • spec.alpha 0.5.238’e güncellendi
    • core.specs.alpha 0.4.74’e güncellendi

REPL’de kütüphane ve araç kullanma özellikleri

  • Geliştirme sırasında JVM’i yeniden başlatmadan kütüphane eklemeyi gerektiren durumlar olabiliyor
    • Deneysel değerlendirme
    • Projeye bilinen bağımlılıkların eklenmesi
    • Belirli bir iş için kütüphane eklenmesi
  • Clojure 1.12, REPL durumunu kaybetmeden kütüphane eklemek için yeni fonksiyonlar sunuyor
    • add-lib: classpath’te olmayan bir lib’i indirip classloader’a ekler
      • Zaten classpath’te olan lib’leri güncellemez
      • Koordinatlar yoksa, en son Maven sürümünü ya da git depo adından çıkarılabiliyorsa en son git sürümünü veya tag’i kullanabilir
    • add-libs: birden fazla yeni kütüphane ve sürümü birlikte çözümler
    • sync-deps: deps.edn içinde olup henüz classpath’te olmayan lib’ler için add-libs çağırır
  • Bu fonksiyonlar yalnızca geliştirme sırasında REPL kullanımı için tasarlandı
    • Production kodu derlemek ve sürdürmek için doğru yol hâlâ deps.edn kullanmak
    • Üç fonksiyon da *repl* değişkeninin true olarak bağlanıp bağlanmadığını kontrol ediyor
    • clojure.main/repl bu bayrağı otomatik olarak bağlıyor
    • clojure.main REPL’inde yeni fonksiyonlar otomatik olarak user namespace’ine refer ediliyor
    • Diğer REPL’lerde (require '[clojure.repl.deps :refer :all]) gerekebilir
  • Kütüphane çözümleme ve indirme işlemleri tools.deps tarafından yapılıyor
    • Geliştirme sırasında proje classpath’ine tools.deps ve bağımlılıklarını eklememek için, Clojure CLI üzerinden fonksiyonları ayrı bir süreçte çağıran yeni bir API de eklendi
  • clojure.tools.deps.interop/invoke-tool, araç fonksiyonlarını ayrı bir süreçte çağırıyor
    • Aracın classpath’i deps.edn içinde tanımlanıyor
    • Araç bağımlılıklarını proje classpath’ine eklemeye gerek yok
    • add-lib özelliği invoke-tool kullanılarak oluşturuldu; bu yapı kullanıcı araçlarını etkileşimli olarak derlemek veya çağırmak için de kullanılabiliyor
    • Fonksiyon yürütme protokolü CLI reference sayfasında yer alıyor

Harici süreç çalıştırma API’si

  • Mevcut clojure.java.shell namespace’ine ek olarak, Java’nın yeni süreç bilgisi, kontrol ve I/O yönlendirme API’sini kullanan bir yol eklendi
  • Clojure 1.12, yeni namespace clojure.java.process’i ekliyor
    • Java’nın yeni süreç API’lerinden yararlanıyor
    • Mevcut yaklaşıma göre daha kolay kullanılacak şekilde tasarlandı
  • Temel fonksiyonlar şunlar
    • start: stream’ler üzerinde tam denetim sağlar ve ileri seviye kullanım için alttaki Java nesnelerine erişim sunar
    • exec: harici süreci çalıştırır ve tamamlandığında stdout’u döndüren yaygın senaryoyu ele alır

Java birlikte çalışabilirliğinin genişletilmesi

  • Method values eklendi; böylece Java metotları higher-order function’larda daha doğrudan kullanılabiliyor
    • Daha önce Java metotlarını map gibi yerlere vermek için bunları elle fonksiyona sarmak gerekiyordu
    • Manuel sarmalama ayrıntılıydı; overload ayrımı için hint gerekebiliyor, ayrıca ek reflection veya boxing oluşabiliyordu
    • Artık qualified methods, değer konumunda normal fonksiyon gibi kullanılabiliyor ve derleyici wrapper function’ı otomatik oluşturuyor
    • Qualified method overload nedeniyle çözülemezse derleyici reflection çağrısı üretiyor
    • Geliştiriciler :param-tags metadata’sıyla istenen tek method signature’ını belirtebiliyor
  • Qualified method sözdizimi sınıfı ve metodu açıkça belirtir
    • Classname/method: statik metodu çağıran Clojure fonksiyon değeri
    • Classname/.method: instance metodu çağıran Clojure fonksiyon değeri
    • Classname/new: constructor çağıran Clojure fonksiyon değeri
    • Statik ve instance metotları ayırt etmek için Classname/method ve Classname/.method sözdizimi kullanılmalı
  • :param-tags metadata’sı overload edilmiş metotların çözümünde kullanılıyor
    • Değer olarak kullanılan qualified method yalnızca sınıf ve metot adını verdiğinden overload edilmiş metot çözülemiyor
    • :param-tags, [tag …] biçiminde bir vektördür ve her tag istenen signature’daki bir parametreye karşılık gelir
    • Overload edilmemiş tiplerin parametreleri için _ placeholder’ı kullanılabilir
    • :param-tags verildiğinde derleyicinin bunu derleme zamanında tek bir metoda çözümleyebilmesi gerekir
    • Yeni metadata reader sözdizimi ^[tag …], üye sembolüne :param-tags metadata’sı ekler
  • Dizi sınıfı sözdizimi eklendi
    • Clojure, sınıf adı sembollerini sınıf nesnesi değeri ve type hint olarak destekliyordu; ancak string dışında dizi sınıfı sözdizimi sunmuyordu
    • Artık ComponentClass/#dimensions biçimindeki sembollerle dizi sınıflarına başvurulabiliyor
    • Örnek: String/1, java.lang.String/1, long/2
    • Bileşen sınıfı tam sınıf adı, import edilmiş sınıf veya primitive olabilir
    • Dizi sınıfı sözdizimi hem type hint hem değer olarak kullanılabilir
  • Java fonksiyonel arayüz birlikte çalışabilirliği geliştirildi
    • Java fonksiyonel arayüzleri @FunctionalInterface ile işaretlenir ve tek bir metoda sahiptir
    • Clojure fonksiyonları, arity uyuyorsa fonksiyonel arayüz alan Java metot çağrılarına geçirilebilir
    • Clojure derleyicisi, Clojure fonksiyonunu gerekli fonksiyonel arayüze örtük olarak dönüştüren bir lambda adapter oluşturur
    • Döngülerde tekrarlanan adapter üretimini önlemek için let bağlama adına hint eklenerek bu dönüşüm açıkça zorlanabilir
  • Supplier birlikte çalışabilirliği de iyileştirildi
    • Değer sağlayan Supplier alan metotları çağırmak için daha önce reify ile adapter yazmak gerekiyordu
    • Clojure’un IDeref uygulamaları olan delay, future, atom vb. artık Supplier arayüzünü doğrudan uyguluyor

Stream işleme ve koleksiyon performansı iyileştirmeleri

  • Java API’lerinin giderek daha sık döndürdüğü Stream’leri Clojure tarzında tüketmek için yeni fonksiyonlar eklendi
    • Clojure 1.12’nin fonksiyonel arayüz desteğiyle birlikte Stream birlikte çalışabilirlik fonksiyonları sunuluyor
    • (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
    • Tüm fonksiyonlar terminal stream işlemleridir ve stream’i tüketir
  • PersistentVector, Java koleksiyonlarının stream uygulamalarında kullanılan spliterator’ı doğrudan sağlıyor
    • Spliterator, daha hızlı paralel dolaşım için bölünebilen bir iterator’dur
    • PersistentVector için yeni özel spliterator paralelliği destekliyor ve performansı belirgin biçimde artırıyor
  • drop, nthrest, nthnext ve partition işleme verimliliği artırıldı
    • CLJ-2713, koleksiyonların sıralı dolaşımdan daha verimli biçimde drop yapabildiğini gösteren iç arayüz IDrop’u ekliyor
    • Bu arayüz persistent collection’larda ve range, repeat gibi algoritmik koleksiyonlarda uygulanıyor
    • Yeni partitionv, partitionv-all, splitv-at fonksiyonları mevcut karşılıklarından daha verimli ve realized seq partition’ları yerine vektör partition’ları üretiyor

Var interning politikasının sıkılaştırılması

  • Bir namespace içinde var interning yapmak, aliasing’den farklı olarak, tüm başvuruların aynı nesneyi almasını sağlayan kararlı bir referans oluşturmaktır
  • Daha önce interned var’ın değiştirilebildiği bazı durumlar vardı; 1.12.0-alpha1 ile politika sıkılaştırıldı
    • Böyle bir durumda "REJECTED: attempt to replace interned var #'some-ns/foo with #'other-ns/foo in some-ns, you must ns-unmap first" biçiminde bir uyarı görünür
  • Bu politika, Clojure 1.11.0’da clojure.core’a yeni fonksiyonlar, özellikle abs, eklenince görünür hâle gelen sorunun kök nedenini ele alıyor
    • Eski Clojure sürümlerinde derlenmiş kod, clojure.core’a sonradan eklenen bir fonksiyon adıyla aynı var adına sahipse, 1.11.0 çalışma zamanında yüklendiğinde unbound hâle gelebiliyordu
    • CLJ-2711’e ek olarak, bu alandaki daha eski bir düzeltme olan CLJ-1604 geri alındı

Tüm değişiklik listesi

  • Clojure 1.12.0’daki tüm değişiklikler resmî changelog içinde yer alıyor

1 yorum

 
GN⁺ 2024-09-06
Hacker News yorumları
  • Gerçekten büyük bir sürüm ve pek çok harika yeni özellik var
    Kişisel olarak en çok add-libs hoşuma gitti. Artık bir sorunu yeniden üretmek için tek dosyalık demolar veya minimal örnekler oluşturulabiliyor; bu da çalıştırılabilir küçük kod parçaları paylaşmanın önündeki engeli ciddi ölçüde azaltıyor
    Java boilerplate’i olmadan Java kütüphaneleri de demo edilebiliyor. REPL’de biraz kurcaladıktan sonra kodu HN yorumu gibi bir yere yapıştırırsanız, herkes aynı “kurulumu” birebir yeniden oluşturup çalıştırabilir. Bir depoyu klonlamaya bile gerek yok

    • Groovy’yi hatırlayan var mı bilmiyorum. Groovy’de, anlatılan add-libs ile fiilen aynı işi yapan @Grab anotasyonu var ve betik yazımı için çok kullanışlı
    • Java’da da artık REPL ve betik desteği var, ancak add-libs benzeri bir özellik henüz kullanılabilir meta komutlar arasında yok
  • Bu sürümü Clojure/conj 2024’e kadar erteleyeceklerini sanmıştım. Buna dair özel bir dayanağım yoktu ama Clojure 1.10, Clojure/conj 2021 civarında çıkmıştı ve Datomic’in ücretsiz hale gelme duyurusu da Clojure/conj 2023’ün başlarında yapılmıştı
    Yine de hâlâ spec2’yi bekliyorum. Şimdilik spec’in katılığını Malli ile aşıyorum, ama Clojure’da birinci sınıf vatandaş değil. Bunun başlıca nedeni makroları denetleyememesi; bu da Clojure derleyicisinin tasarımında bilinçli bir tercih. Yine de Malli şemalarını veri olarak manipüle ederseniz schema/select fikrini taklit etmek mümkün
    Fonksiyonel arayüz değişikliği sayesinde artık (defmacro ->Consumer [f] ...) gibi yardımcı makroları tutmaya gerek kalmadan fonksiyonu doğrudan geçirmek mümkün

    • “maybe not”u görünce schema/select’e gerçekten ihtiyaç duydum ve Malli için bir kütüphane yazdım: https://github.com/eval/malli-select
    • “Makroları denetleyememek” derken ne kastedildiğini merak ediyorum. spec ile makrolara s/fdef eklenebiliyor ve çağrılar derleme zamanında denetleniyor
      Aslında Clojure core da makro çağrılarını spec ile denetliyor; yanlış çağrıldığında bazen yığın izinde bunun izlerini görebiliyorsunuz. Başka bir anlamda mı söylendiğini merak ediyorum
  • Bir sürü yeni özellik gelmesine rağmen mevcut kodun aynen çalışmaya devam etmesi gerçekten güzel. Uyumluluğu bozan değişikliklerden kaçınmaya yönelik istikrarlı çaba kendini gösteriyor

  • Clojure hakkında daha fazla bilgi edinmek isterseniz 23-25 Ekim’de Virginia, Alexandria’da düzenlenecek Clojure/conj konferansına bakabilirsiniz: https://2024.clojure-conj.org

  • add-libs ve sync-deps’in gelmesine sevindim. Artık bir oturumu kapatmak için neredeyse hiç neden yok gibi, hatta hiç yok gibi görünüyor
    Bu sürüm, önceki sürümlere kıyasla kapsam olarak epey farklı görünüyor ve içine girenlerin çokluğu onu ilginç kılıyor. Yine de hızlandığı için birkaç sürüm sonra iç içe geçmiş karmaşık bir yumağa dönüşmemesini umuyorum

    • Rich Hickey ve Clojure Team çok dikkatli tasarımcılar; bence fazla endişelenmeye gerek yok
    • Tamamen spekülasyon ama Rich Hickey’nin nubank’ten ayrıldıktan sonraki ilk sürüm olduğu için belki de daha fazla ilgi gösterebilmiştir
  • Fonksiyonel arayüz değişikliği çok büyük. Clojure, özenli birlikte çalışabilirlik sayesinde Java’ya yakın durduğunda en iyi halinde oluyor ve bu değişiklik büyük bir boşluğu kapatıyor

  • spec’e ne olduğunu merak ediyorum. Terk mi edildi? Beklemeye değer bir haber var mı merak ediyorum

    • spec hâlâ var ve kullanılıyor. Devam çalışmaları da çok yapıldı, ancak çeşitli konularda ne yapılacağı değerlendirilirken durmuş durumda
  • Oldukça sağlam bir sürüm gibi görünüyor ve Clojure’un hâlâ iyi durumda olmasına sevindim

    • Gerçekten iyi durumda mı merak ediyorum. Yeni bir projede kullanmayı değerlendiriyorum ve Clara[0] ile birlikte düşünüyorum. Ancak eskisi kadar ana akım değil gibi ve ekosistem de önceye göre daha seyrekleşmiş hissi veriyor
      Trollemek istemiyorum. Seçmek istiyorum ve mühendislik açısından iyi bir karar gibi görünüyor. Ama popülerliği ve katkıda bulunanları hızla düşüyorsa yakın gelecekte sorun çıkarabilir
      [0] https://www.clara-rules.org/
  • Mevcut geliştiricileri Clojure geliştiricisine dönüştürmek hiç olmadığı kadar kolaylaştı
    Başta yaşanan büyük sorun[0] kodu okumak; ChatGPT veya Claude gibi yapay zeka araçları mevcut Clojure kodunu açıklamakta çok başarılı. Bunun sonucunda geliştirici onboarding’i çok daha hızlı olabilir
    [0] Birkaç hafta sonra Clojure okumak doğal hale geliyor ve eskiden okuyamadığınızı bile unutuyorsunuz

  • Pek çok güzel iyileştirme var. Genelde elimin gittiği başlıca Lisp ailesi dili