2 poin oleh GN⁺ 2024-09-06 | 1 komentar | Bagikan ke WhatsApp
  • Clojure 1.12.0 diumumkan sebagai rilis terakhir berbasis Java 8 sebelum rilis berikutnya memindahkan kompatibilitas Java minimum dan baseline bytecode ke Java LTS yang lebih baru, sambil tetap mempertahankan bytecode Java 8
  • Di lingkungan virtual thread JDK 21, lazy-seq dan delay menggunakan lock alih-alih synchronized, sehingga mengurangi situasi ketika blocking I/O membuat thread nyata ter-pin
  • Di REPL, library dapat ditambahkan dengan add-lib, add-libs, dan sync-deps tanpa me-restart JVM, tetapi fitur ini dibatasi untuk penggunaan interaktif saat pengembangan
  • Interoperabilitas Java diperluas dengan penambahan method values, :param-tags, sintaks kelas array, konversi functional interface, Supplier, dan fungsi pemrosesan Java Stream
  • Dari sisi performa dan kompatibilitas, rilis ini mencakup spliterator PersistentVector, pemrosesan drop/partisi yang efisien, pengetatan kebijakan Var interning, perbaikan CVE-2024-22871, serta perapihan identifier serialisasi Java

Kompatibilitas Java 8, keamanan, dan perapihan serialisasi

  • Informasi unduhan dan penggunaan Clojure 1.12.0 dapat dilihat di halaman Downloads
  • Baseline Java 8 tetap dipertahankan pada rilis ini
    • Clojure 1.12 menghasilkan bytecode Java 8, sama seperti Clojure 1.10 dan 1.11
    • Rilis berikutnya direncanakan memindahkan bytecode dan kompatibilitas Java minimum ke rilis Java LTS yang lebih baru
  • Masalah pinning virtual thread pada JDK 21 telah dikurangi
    • Sebelum 1.12, lazy-seq dan delay menjalankan kode pengguna di dalam blok synchronized untuk menjamin perilaku yang hanya dieksekusi sekali
    • Pada JDK 21, synchronized belum berpartisipasi dalam cooperative blocking, sehingga jika kode tersebut melakukan blocking I/O, thread nyata dapat ter-pin
    • Saat menggunakan -Djdk.tracePinnedThreads=full, JDK 21 dapat mengeluarkan peringatan untuk situasi ini
    • Pada 1.12, lazy-seq dan delay menggunakan lock alih-alih blok synchronized
  • Perbaikan keamanan untuk CVE-2024-22871 telah diterapkan, dengan advisory terkait di GHSA-vr64-r9qj-h27f
  • serialVersionUID untuk kelas-kelas terkait serialisasi Java ditetapkan secara eksplisit
    • Tipe data Clojure telah mengimplementasikan antarmuka serialisasi Java sejak Clojure 1.0
    • Serialisasi Java hanya berfungsi saat identifier yang dibuat berdasarkan nama kelas, hierarki tipe, dan field serialisasi cocok pada saat deserialisasi
    • Clojure tidak menjamin konsistensi serialisasi antarversi, tetapi perubahan ini diterapkan untuk meningkatkan kontrol ke depan agar kompatibilitas tidak rusak lebih dari yang diperlukan
  • Dependensi juga diperbarui
    • spec.alpha diperbarui ke 0.5.238
    • core.specs.alpha diperbarui ke 0.4.74

Fitur untuk menangani library dan tool di REPL

  • Saat pengembangan, ada situasi ketika library perlu ditambahkan tanpa me-restart JVM
    • Evaluasi eksperimental
    • Menambahkan dependensi yang diketahui ke proyek
    • Menambahkan library untuk tugas tertentu
  • Clojure 1.12 menyediakan fungsi-fungsi baru untuk menambahkan library tanpa kehilangan state REPL
    • add-lib: mengunduh lib yang belum ada di classpath dan menambahkannya ke classloader
      • Tidak memperbarui lib yang sudah ada di classpath
      • Jika koordinat tidak ada, fungsi ini menggunakan versi Maven terbaru atau versi/tag git terbaru jika nama repository git dapat disimpulkan
    • add-libs: menyelesaikan beberapa library baru beserta versinya secara bersamaan
    • sync-deps: memanggil add-libs untuk lib yang ada di deps.edn tetapi belum ada di classpath
  • Fungsi-fungsi ini hanya ditujukan untuk penggunaan REPL saat pengembangan
    • Cara yang benar untuk membangun dan memelihara kode produksi tetap menggunakan deps.edn
    • Ketiga fungsi tersebut memastikan bahwa *repl* terikat ke true
    • clojure.main/repl mengikat flag ini secara otomatis
    • Di REPL clojure.main, fungsi-fungsi baru tersebut otomatis di-refer ke namespace user
    • Pada REPL lain, (require '[clojure.repl.deps :refer :all]) mungkin diperlukan
  • Resolusi dan pengunduhan library ditangani oleh tools.deps
    • Agar tools.deps dan dependensinya tidak perlu dimasukkan ke classpath proyek saat pengembangan, API baru juga ditambahkan untuk memanggil fungsi melalui Clojure CLI dalam proses terpisah
  • clojure.tools.deps.interop/invoke-tool memanggil fungsi tool dalam proses terpisah
    • Classpath tool didefinisikan di deps.edn
    • Dependensi tool tidak perlu ditambahkan ke classpath proyek
    • Fitur add-lib dibuat menggunakan invoke-tool, dan juga dapat digunakan untuk membangun atau memanggil tool pengguna secara interaktif
    • Protokol eksekusi fungsi dapat dilihat di CLI reference

API eksekusi proses eksternal

  • Selain namespace clojure.java.shell yang sudah ada, kini tersedia jalur yang memanfaatkan API baru Java untuk informasi, kontrol, dan pengalihan I/O proses
  • Clojure 1.12 menambahkan namespace baru clojure.java.process
    • Memanfaatkan API terkait proses baru dari Java
    • Dirancang agar lebih mudah digunakan dibandingkan pendekatan lama
  • Fungsi utamanya adalah sebagai berikut
    • start: memberikan kontrol penuh atas stream dan akses ke objek Java dasar untuk penggunaan tingkat lanjut
    • exec: menangani kasus umum menjalankan proses eksternal dan mengembalikan stdout saat selesai

Perluasan interoperabilitas Java

  • Method values ditambahkan sehingga method Java dapat digunakan lebih langsung dalam higher-order function
    • Sebelumnya, untuk meneruskan method Java ke map dan sejenisnya, method harus dibungkus sendiri sebagai fungsi
    • Wrapping manual bersifat verbose, sering membutuhkan hint untuk membedakan overload, atau dapat menimbulkan refleksi dan boxing tambahan
    • Kini qualified methods dapat digunakan seperti fungsi biasa pada posisi value, dan compiler otomatis menghasilkan fungsi pembungkus
    • Jika qualified method tidak dapat di-resolve karena overload, compiler menghasilkan pemanggilan reflektif
    • Developer dapat menentukan satu signature method yang diinginkan melalui metadata :param-tags
  • Sintaks qualified method menyebutkan kelas dan method secara eksplisit
    • Classname/method: nilai fungsi Clojure yang memanggil method statis
    • Classname/.method: nilai fungsi Clojure yang memanggil method instance
    • Classname/new: nilai fungsi Clojure yang memanggil constructor
    • Untuk membedakan method statis dan method instance, sintaks Classname/method dan Classname/.method harus digunakan
  • Metadata :param-tags digunakan untuk resolve method yang di-overload
    • Qualified method yang digunakan sebagai value hanya menyediakan kelas dan nama method, sehingga method overload tidak dapat di-resolve
    • :param-tags adalah vector berbentuk [tag …], dan setiap tag berkorespondensi dengan parameter pada signature yang diinginkan
    • Placeholder _ dapat digunakan untuk parameter dengan tipe yang tidak di-overload
    • Jika :param-tags diberikan, compiler harus dapat me-resolve ke satu method pada waktu kompilasi
    • Sintaks reader metadata baru ^[tag …] menambahkan metadata :param-tags ke symbol member
  • Sintaks kelas array ditambahkan
    • Clojure telah mendukung symbol nama kelas sebagai nilai objek kelas dan type hint, tetapi tidak menyediakan sintaks kelas array selain string
    • Kini kelas array dapat dirujuk dengan symbol berbentuk ComponentClass/#dimensions
    • Contoh: String/1, java.lang.String/1, long/2
    • Kelas komponen dapat berupa nama kelas lengkap, kelas yang di-import, atau primitive
    • Sintaks kelas array dapat digunakan baik sebagai type hint maupun sebagai value
  • Interoperabilitas functional interface Java ditingkatkan
    • Functional interface Java diberi @FunctionalInterface dan memiliki satu method
    • Fungsi Clojure dapat diteruskan ke pemanggilan method Java yang menerima functional interface jika arity-nya cocok
    • Compiler Clojure membuat lambda adapter untuk secara implisit mengonversi fungsi Clojure ke functional interface yang diperlukan
    • Untuk menghindari pembuatan adapter berulang di dalam loop, Anda dapat memaksa secara eksplisit dengan memberi hint pada nama binding let
  • Interoperabilitas Supplier juga ditingkatkan
    • Untuk memanggil method yang menerima Supplier yang memasok nilai, sebelumnya adaptor harus ditulis dengan reify
    • Implementasi IDeref Clojure seperti delay, future, dan atom kini mengimplementasikan interface Supplier secara langsung

Pemrosesan Stream dan peningkatan performa collection

  • Fungsi-fungsi untuk mengonsumsi Stream, yang semakin sering dikembalikan oleh Java API, dengan gaya Clojure telah ditambahkan
    • Bersama dukungan functional interface di Clojure 1.12, tersedia fungsi interoperabilitas Stream
    • (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
    • Semua fungsi merupakan operasi stream terminal dan mengonsumsi stream
  • PersistentVector menyediakan spliterator langsung yang digunakan dalam implementasi stream Java collection
    • Spliterator adalah iterator yang dapat dibagi untuk traversal paralel yang lebih cepat
    • Spliterator kustom baru milik PersistentVector mendukung paralelisme dan memberikan peningkatan performa yang besar
  • Efisiensi pemrosesan drop, nthrest, nthnext, dan partisi ditingkatkan
    • CLJ-2713 menambahkan interface internal IDrop yang menunjukkan bahwa collection dapat melakukan drop lebih efisien daripada traversal sekuensial
    • Interface ini diimplementasikan pada persistent collection dan collection algoritmik seperti range dan repeat
    • Fungsi baru partitionv, partitionv-all, dan splitv-at lebih efisien daripada fungsi padanannya yang sudah ada, serta menghasilkan partisi vector alih-alih partisi realized seq

Pengetatan kebijakan Var interning

  • Meng-intern var dalam namespace, berbeda dari aliasing, adalah membuat referensi stabil agar semua reference mendapatkan objek yang sama
  • Sebelumnya ada beberapa kasus ketika interned var dapat diganti, dan pada 1.12.0-alpha1 kebijakan ini diperketat
    • Jika situasi ini terjadi, peringatan berbentuk "REJECTED: attempt to replace interned var #'some-ns/foo with #'other-ns/foo in some-ns, you must ns-unmap first" akan muncul
  • Kebijakan ini menangani akar masalah yang terlihat ketika fungsi baru, terutama abs, ditambahkan ke clojure.core pada Clojure 1.11.0
    • Jika kode yang dikompilasi dengan versi Clojure sebelumnya memiliki nama var yang sama dengan nama fungsi yang baru ditambahkan ke clojure.core, var tersebut dapat menjadi unbound saat dimuat di runtime 1.11.0
    • Selain CLJ-2711, perbaikan sebelumnya di area ini, CLJ-1604, juga di-rollback

Daftar perubahan lengkap

1 komentar

 
GN⁺ 2024-09-06
Komentar Hacker News
  • Ini benar-benar rilis besar dan punya banyak fitur baru yang keren
    Secara pribadi, saya paling suka add-libs. Sekarang kita bisa membuat demo satu file atau contoh minimal untuk mereproduksi isu, sehingga hambatan untuk membagikan potongan kode kecil yang bisa dijalankan jadi jauh lebih rendah
    Library Java juga bisa didemokan tanpa boilerplate Java. Setelah mencoba-coba di REPL, tempelkan kodenya di tempat seperti komentar HN, lalu siapa pun bisa mereproduksi dan menjalankan “setup” yang sama persis. Tidak perlu meng-clone repositori

    • Entah masih ada yang ingat Groovy atau tidak. Groovy punya anotasi @Grab yang pada dasarnya melakukan hal yang sama seperti add-libs yang dijelaskan, dan itu sangat praktis untuk menulis skrip
    • Java sekarang juga punya dukungan REPL dan scripting, tetapi fitur seperti add-libs belum ada di antara meta-command yang tersedia
  • Saya kira rilis ini akan ditunda sampai Clojure/conj 2024. Tidak ada dasar khusus, tetapi Clojure 1.10 keluar sekitar Clojure/conj 2021, dan pengumuman Datomic gratis juga terjadi di awal Clojure/conj 2023
    Meski begitu, saya masih menunggu spec2. Saat ini saya mengakali kekakuan spec dengan Malli, tetapi itu bukan warga kelas satu di Clojure. Terutama karena macro tidak bisa diperiksa, dan ini memang bagian yang disengaja dalam desain compiler Clojure. Namun, jika skema Malli dimanipulasi sebagai data, ide schema/select bisa ditiru
    Berkat perubahan functional interface, sekarang fungsi bisa langsung diteruskan tanpa perlu mempertahankan macro utilitas seperti (defmacro ->Consumer [f] ...)

    • Setelah melihat “maybe not”, saya benar-benar membutuhkan schema/select, jadi saya membuat library untuk Malli: https://github.com/eval/malli-select
    • Saya penasaran apa maksudnya “macro tidak bisa diperiksa”. Dengan spec, macro bisa diberi s/fdef, dan pemanggilannya diperiksa pada waktu kompilasi
      Faktanya, Clojure core juga memeriksa pemanggilan macro dengan spec, sehingga ketika pemanggilannya salah, terkadang jejaknya bisa terlihat di stack trace. Saya penasaran apakah yang dimaksud berbeda
  • Sangat menyenangkan melihat kode lama tetap berjalan apa adanya meski ada banyak fitur baru. Upaya konsisten untuk menghindari breaking compatibility benar-benar terlihat hasilnya

  • Jika ingin tahu lebih banyak tentang Clojure, ada baiknya melihat konferensi Clojure/conj yang diadakan pada 23–25 Oktober di Alexandria, Virginia: https://2024.clojure-conj.org

  • Senang melihat add-libs dan sync-deps masuk. Sekarang tampaknya hampir tidak ada, atau bahkan sama sekali tidak ada, alasan untuk menutup sesi
    Rilis ini tampak cukup berbeda cakupannya dibanding rilis-rilis sebelumnya, dan menarik karena isinya banyak. Namun, karena lajunya lebih cepat, semoga beberapa rilis ke depan tidak berubah menjadi gumpalan yang kusut

    • Rich Hickey dan Clojure Team adalah perancang yang sangat berhati-hati, jadi menurut saya tidak perlu terlalu khawatir
    • Ini murni spekulasi, tetapi karena ini rilis pertama setelah Rich Hickey meninggalkan nubank, mungkin ia bisa mencurahkan lebih banyak perhatian
  • Perubahan functional interface sangat besar. Clojure paling bagus ketika tetap dekat dengan Java lewat interoperabilitas yang hati-hati, dan perubahan ini menutup satu celah besar

  • Saya penasaran apa kabar spec. Apakah sudah ditinggalkan? Saya ingin tahu apakah ada kabar yang bisa dinantikan

    • spec masih ada dan digunakan. Banyak pekerjaan lanjutan juga sudah dilakukan, tetapi saat ini tertahan sambil mempertimbangkan apa yang harus dilakukan pada beberapa hal
  • Ini terlihat seperti rilis yang cukup solid, dan saya senang Clojure masih berjalan dengan baik

    • Saya penasaran apakah memang benar masih berjalan dengan baik. Saya sedang mengevaluasinya untuk proyek baru, dan mempertimbangkannya bersama Clara[0]. Namun, sepertinya tidak se-mainstream dulu dan ekosistemnya terasa lebih jarang dibanding sebelumnya
      Bukan bermaksud trolling. Saya ingin memilihnya, dan secara engineering tampak seperti keputusan yang bagus. Tetapi jika popularitas dan kontributornya sedang turun drastis, itu bisa menjadi penghambat dalam waktu dekat
      [0] https://www.clara-rules.org/
  • Mengubah developer yang sudah ada menjadi developer Clojure kini lebih mudah daripada sebelumnya
    Masalah besar di awal[0] adalah membaca kode, dan AI seperti ChatGPT atau Claude sangat bagus dalam menjelaskan kode Clojure yang sudah ada. Akibatnya onboarding developer bisa jauh lebih cepat
    [0] Setelah beberapa minggu, membaca Clojure terasa alami, dan kita bahkan lupa bahwa dulu pernah tidak bisa membacanya

  • Banyak peningkatan yang keren. Biasanya ini adalah bahasa keluarga Lisp utama yang saya pilih