Jepsen neden Clojure ile yazıldı?
(aphyr.com)- Jepsen, veritabanları gibi eşzamanlı sistemleri doğrulayan bir araç olduğundan, uygulama dili seçilirken eşzamanlılık modeli ve gerçek thread desteği de önemli ölçütlerdi
- Clojure’un değişmez ve kalıcı veri yapıları ile JVM eşzamanlılık araçları, test kodunda hata olasılığını azaltıyor ve Java tabanlı veritabanı istemcileriyle de iyi uyum sağlıyor
- Jepsen çalışması, belirli bir ürün geliştirmekten çok deney ve keşfe yakın olduğu için REPL, makrolar, threading macro ve EDN gibi Clojure özellikleri karmaşık veri işlemeyi kolaylaştırdı
- Performans en üst düzeyde olmasa da, idiomatic Clojure genelde Java’dan yalnızca bir iki mertebe içinde daha yavaş kalıyor; gerekli noktalarda optimizasyon ve JVM profiling araçları ile bu yönetilebiliyor
- Küçük topluluk, yaygın biçimde başarılı olmuş bir statik tip sistemi bulunmaması, primitive işlemedeki zorluklar ve hata mesajları gibi zayıf yönler olsa da, 1–3 kişinin bakımını yaptığı ve kullandığı Jepsen için bu kabul edilebilir bir ödünleşimdi
Eşzamanlılık test aracına uygun dil gereksinimleri
- Jepsen, eşzamanlı sistemleri test etmek için yapıldı ve test edilen hedefler çoğunlukla veritabanlarıdır
- Clojure, eşzamanlılık test kodu yazarken gereken temel koşulları iyi karşılıyor
- Değişmez ve kalıcı veri yapıları, paylaşılan durumla çalışan kodlarda hata olasılığını düşürüyor
- Gerçek thread’ler, promise, future, atom, lock, queue, cyclic barrier ve
java.util.concurrentkullanılabiliyor - Haskell gibi yan etki denetimi daha sıkı olan diller de değerlendirildi, ancak Clojure’un daha az dogmatik yaklaşımı Jepsen’e daha uygun bulundu
- Veritabanı testleri için çeşitli istemcilerle bağlantı kurmak gerekiyor
- Neredeyse tüm veritabanları genellikle Java ile yazılmış JVM istemcileri sunuyor
- Clojure, Java birlikte çalışabilirliği güçlü olduğu için bu istemcilerden yararlanmayı kolaylaştırıyor
Deney ve veri keşfi için avantajlı Clojure özellikleri
- Jepsen testleri deneysel bir çalışma olduğundan, kısa, kolay değiştirilebilir ve prototiplemeye uygun bir dile ihtiyaç vardı
- Clojure’un özlü sözdizimi ve makro sistemi bu çalışma biçimine iyi uyuyor
- threading macro, ardışık dönüşümleri daha okunabilir hale getiriyor
- Makrolar, yeniden kullanılabilir hata işleme ve kaynak kapsamı kontrolünü uygulamayı kolaylaştırıyor
- Clojure REPL, test çalıştırmalarından çıkan verileri anında keşfetmek için yararlı
- Test sürecinde karmaşık ve iç içe geçmiş veri yapılarını ifade etmek, dönüştürmek ve incelemek gerekiyor
- Clojure’un veri yapıları ve standart kütüphane fonksiyonları bu işte güçlü
- Konsola ve dosyalara çok sayıda yapılandırılmış veri çıktısı verirken EDN veri sözdizimi iyi uyuyor
Performans ve uzun vadeli bakım arasındaki ödünleşim
- Jepsen çok büyük bir sistem olmasa da makul miktarda veri işlediği için “yeterince iyi” performansa ihtiyaç duyuyor
- Clojure en hızlı dil değil
- İdiomatic Clojure, genelde Java’ya kıyasla yalnızca bir veya iki mertebe içinde performans farkı gösteriyor
- Önemli darboğazlarda bu fark azaltılabiliyor
- JVM’in profiling araçları Clojure ile iyi çalışıyor
- Jepsen yaklaşık 10 yıllık bir proje olduğu için olgun bir çekirdek ve kararlılık önemliydi
- Clojure hem JVM hedefi olarak hem de dilin kendisi açısından kararlı
- Kütüphaneler Scala ya da Ruby’de olduğu kadar hızlı “çürümüyor”
Katlanılması gereken Clojure zayıflıkları
- Clojure’un, Jepsen ölçeğinde yönetilebilir olsa da daha büyük ekipler için yük oluşturabilecek dezavantajları var
- Mühendislik topluluğu küçük
- Yaygın kabul görmüş ve başarılı bir statik tip sistemi yok
- Jepsen aynı anda yalnızca 1–3 kişi tarafından bakımı yapılıp kullanıldığı için bu kısıt daha az etkili oluyor
- JVM primitive’leriyle çalışırken Java’ya inmeden ilerlemek zorlayıcı olabiliyor; gerçekten de zaman zaman Java kullanılıyor
- Polimorfizm sisteminin bazı kısımları yetersiz, ancak kütüphanelerle aşılabiliyor; hata mesajları da iyi değil
- Jepsen, birden fazla dilde prototiplendikten sonra Clojure’da karar kılındı ve 10 yıl sonra bile hâlâ oldukça iyi bir ödünleşim olarak görülüyor
1 yorum
Hacker News yorumları
Yaklaşık 10 yıldır Clojure/ClojureScript ile SaaS işleten biri olarak Kyle’ın yazısına tamamen katılıyorum
Benim için ayrıca çok değerli olan şey, alan kodunun büyük bölümünü cljc dosyalarında tutup hem sunucuya hem istemciye derleyebilmek; transducer’ların ve transducer pipeline’larının performans, bileştirilebilirlik ve yeniden kullanılabilirlik açısından hafife alınması; ayrıca Rich Hickey’nin tasarladığı bir dilden bekleneceği gibi istikrarının ve uzun vadeli uyumluluğunun çok iyi olmasıydı
Geriye dönük uyumluluk çok önemli; bu sayede sürekli değişen araçların peşinden sürüklenmek yerine uygulamaya odaklanabiliyoruz. Ortalama bir Clojure programcısının 12 yıldan fazla deneyime sahip olup üst çeyrekte ücret almasının da tesadüf olmadığını düşünüyorum
Neredeyse hiç bozulmuyor ve yükseltmesi de kolay. HMR, REPL ve cljc dosyaları sayesinde geliştirici deneyimi de harika; Clojure kodu yazmak keyifli
Çalışan şeyleri bozmamak için dili ve sistemleri geriye doğru eğip bükmek kabul ediliyor da, gerçekten çalışan yazılımların büyük bir kısmını taşıyan bu iki dilden neden görkemli geçmişleriyle bugünlerini koparmaları bekleniyor, anlamıyorum
Clojure’un en büyük sorunu kesinlikle ekosistem
Erişilebilir ve kullanılabilir araçların dağılımına bakınca, ya aşırı solda kalıp korkunç ama erişmesi kolay araçlar oluyor ya da aşırı sağda kalıp README’sinde Yüzüklerin Efendisi alıntısı bulunan, her şeyin n’inci dereceye kadar veri olduğu ve gözleri bağlı, baş aşağı asılı halde Emacs eklentisi yazabilen ejderhaların erişebildiği türden araçlar oluyor
Dilin kendisi harika ve Java birlikte çalışabilirliği de Aphyr’in söylediğinden çok daha iyi; ama Clojure’da Rails ya da Django, hatta daha iyisi Phoenix gibi bir şey olsaydı ikna etmesi çok daha kolay olur ve çok daha yaygın kullanılırdı gibi geliyor
Özellikle Algol ailesinden imperative diller kullanan bir ekibi Lisp’e geçmeye ikna etmek istiyorsanız, elde edilen kazanımın çekilen zahmetten büyük olması gerekir; oysa şu anda HTTP routing gibi temel unsurlar dışında neredeyse her projede tekerleği yeniden icat etmek gerekiyor
Yine de herhangi bir Maven paketini kullanabilmek ve jar olarak derlenebilmek büyük bir avantaj; jar’ın çalıştığı yerde Clojure da çalışıyor
https://calva.io
Clojure’un çeşitli biçimleri var; başlıca Clojure ve ClojureScript kullanılıyor. Babashka, Scittle gibi şeyler de var ama hepsi clj veya cljs’den türemiş
Geliştirme araçları platforma ve IDE tercihine göre değişiyor; Emacs, Neovim, VSCode ve IntelliJ için araçlar farklı, build sistemleri de ihtiyaca göre değişiyor
clj tarafında şu anda en yaygın kullanılan deps.edn, ardından leiningen geliyor; cljs tarafında ise shadow-cljs dışında da birçok seçenek var
Yeni başlayan biri için bunaltıcı olabilir ama topluluk iyi ve her zaman yardım ediyor. Birçok dilde profesyonel programlama yaptım ama Clojure açık ara en sevdiğim
2013 civarından beri Clojure kullanıyorum ama böyle bir araç aklıma gelmiyor. Popüler kütüphanelerin çoğu harika; kararlılık, geriye dönük uyumluluk ve sadelik açısından bildiğim çoğu ekosistemin çok ilerisinde
Clojure ve ClojureScript arasında paylaşılabilen .cljc kaynak dosyaları sayesinde aynı Clojure kodu hem frontend’de hem backend’de çalıştırılabilir
Rich Hickey’nin dediği gibi yalnızca JVM’e “erişmekle” kalmaz, JavaScript’e de erişir
Emacs’i uzun zamandır kullanıyorum ve istediğim gibi ayarlayabiliyor ya da gereken elisp kodunu eninde sonunda yazabiliyorum; ama elisp’te iyi olduğumu kesinlikle söyleyemem
Hiç Lisp lehçesi öğrenmemiş birinin Emacs, elisp ve Clojure’u aynı anda öğrenmesinin zor ve ikna etmesinin de çok güç olduğunu kabul ediyorum
Artık Clojure LSP sunucusu var ve yalnızca Emacs’te değil başka IDE’lerde de çalışıyor; dolayısıyla birçok Clojure geliştiricisi Emacs kullansa bile Clojure geliştirmek için Emacs şart değil
7 yıldır yalnızca Clojure kullanıyorum ve 500 bin satırı aşan bir Clojure + ClojureScript kod tabanıyla aktif olarak çalışıyorum; sağlam bir tip sisteminin olmaması en büyük şikâyetim
Clojure’un ifade gücünün yüksek olduğu, hızlı ilerlemeyi sağladığı ve yolunuza çıkmadığı görüşüne katılıyorum; ama yeniden üretmesi ve debug etmesi zor, dağınık runtime hatalarına da izin veriyor
Yaygın kabul görmüş ve başarılı bir statik tip sistemi olsaydı Clojure muazzam olurdu. Şu anda Ruby ya da Python’u sevip daha güçlü bir şey isteyenler için oldukça iyi olduğunu düşünüyorum; artık Go’nun doğru yol olup olmadığını sorgulamaya başladım
Clojure(Script)’in gerçekten bir nimet gibi olduğuna ve küçük ya da büyük pek çok proje için harika bir seçenek olduğuna tamamen katılıyorum
Üniversite ödevlerinde, sistem yönetiminde, trafik dökümü analizlerinde vb. C, Python, PowerShell, Java, Bash ve SQL’i biraz biraz kullandıktan sonra profesyonel programlamaya ilk başladığım dil Clojure oldu
OrgPad’i Clojure+ClojureScript ile geliştiriyoruz ve kod 100 bin satırı geçse de bunun iyi bir karar olduğunu düşünüyorum
Yaklaşması kolay; biraz kod yazınca, hayal ettiğim gibi çalışacağına dair oldukça güçlü bir güven duyuyorum. Python, PowerShell ve Bash’te bu seviyede değildi; tamamen başka bir lig gibi geliyor
REPL çalışma akışı da gerçekten iyi; bir meslektaşım bununla ilgili bir video hazırladı: https://www.youtube.com/watch?v=4igO7Qbyj9o
Günümüzde Bash’ten Python’a geçmeyi düşündürebilecek fonksiyonları, kısa işleri ve küçük projeleri de Babashka/nbb ile halledebiliyorsunuz
Geçmişte Clojure ve ClojureScript ile birkaç bin satır yazmayı denediğimde, en büyük sorun araçların çoğunun oldukça dağınık hata durumlarına sahip olmasıydı
Python’da da benzer bir yön var; çoğu Python programını Ctrl+C ile kesince içinde ne olduğu belirsiz şeylerin karıştığı bir stack trace çıkar, ama yeni başlayan biri için ilerlemek zordu
Dilin ve ekosistemin içinde yenilikçi ve ilginç birçok fikir olduğunu düşünüyorum. En azından Clojure’un tarih makalesini okuyup derslerin çoğunu içselleştirmeye değer
Herkese sapkınlık gibi gelecek ama Clojure kadar performansa ve kod yazma ergonomisine ciddi yaklaşan, fakat parantezsiz bir betik dili için küçük bir ekosistem alanı olduğunu düşünüyorum
nil’i özel ele alan gelenek olmasa da olurdu, ama en azından bunun bir miktar dayanağı var
(f x y)’ninf(x, y)’den daha iyi olduğunu biliyorum; benim derdim daha çok let formlarının girintiye yol açtığı durumlara yakın. Bir değere isim vermek için hem girinti hem de parantez maliyeti ödemek gerekmediğini düşünüyorumPython gibi, zaten koda koyacağınız girintiyi bilgi olarak kullanan bir dilden gelince, bunlar bazen sadece gürültü gibi hissedilebiliyor
Fonksiyonel stilde, fonksiyonlar arasında aktarılan ara argümanlara isim verme ihtiyacı pek olmaz
Genellikle threading makroları olan
->,->>ile argümanlar bir sonraki fonksiyona örtük olarak geçirilir ya da dönüşümün kendisi saf fonksiyon olarak tanımlanıp en üst seviyeye konurYine de bazı birlikte çalışabilirlik kodlarında çok fazla let gerektiğini anlıyorum. Özellikle prosedürel zincirlerde argümanları değiştiren Java JWT kütüphanelerinde böyle
Uzun süre debug ettikten sonra yeniden yüklemediğinizi ya da yeniden başlatmadığınızı fark etmek gibi
Çoğu zaman bunun, “böyle bir şey olamaz” dediğiniz durumlarda kontrol edilecekler listesine eklendiğini hissediyorum. Örneğin “kod değişikliğimin gerçekten çağrılıp çağrılmadığını görmek için debug çıktısı ekleyeyim” gibi
Elbette bu sadece bir güvenlik ağı; genelde farklı REPL çalışma alışkanlıklarıyla o durumlardan kaçınmaya çalışıyorum
(+ clojure clojurescript)diye okunması gerekiyormuş gibi geldiği için zihnim acı çekiyorClojure birçok şeyi doğru yapıyor ve bu yüzden onu çok seviyorum
Gerçek ekiplerin gerçek web işlerini Clojure ile yaparken hızını kesen şeylerden biri, her biri farklı yaklaşıma sahip sayısız kütüphaneyi birleştirip her şeyi birbirine bağlamaya yönelik neredeyse dogmatik istek bence
Topluluğun mevcut yaklaşımına ek olarak, aktif biçimde sürdürülen, pilleri dahil ve genel olarak kullanılabilir bir web framework’ü de olursa Clojure’un çok daha popüler ve kullanışlı olacağını düşünüyorum. Biff’in bu rolü üstlenip üstlenmediğini merak ediyorum
Dili sevip de kendi canavarını monte etme yöntemini sevmeyen insanlar için alternatif bir yol olsa iyi olurdu
Ruby on Rails’e alışmış olmamın da payı var
Meşhur Rich Hickey konuşmasındaki gibi: complect etmeyin, compose edin yaklaşımı: (https://github.com/matthiasn/talk-transcripts/blob/master/Hi...)
Clojure’u, grafik veritabanlarını ve duruma göre HTMX’i öğrenmek epey fazla. Yine de birçok ayar önceden yapılmış; kimlik doğrulama özenli ve kolay, sunucu yapılandırma dosyasındaki yorumlar da iyi
Linux sunucusu kuran ve SSH ile production REPL’e giren dağıtım script’i de var. XTDB kullanmak istiyorsanız Biff’in belge doğrulama ayarlarını yapma biçimi gerçekten iyi
Şahsen bunu ClojureScript ile çalışan uygulamalardan daha çok seviyorum. React’i saran çok sayıda ClojureScript wrapper’ı var ve neredeyse meme’e dönüştüler; ama bir React geliştiricisi olarak hiçbirini React’ten daha kolay bulmadım
Aksine karmaşık ve ele pek oturmayan bir his veriyor. Buna karşılık HTMX/Hyperscript, HTML sözdizimi manipüle etmesi kolay diziler ve nesnelerden ibaret olduğu için Clojure ile çok iyi uyuşuyor
Clojure sevenlerin tekrarlayıp durduğu “kod veridir” özdeyişi ilk kez gerçek gibi hissettirdi
Eskiden OCaml'i birkaç kez azar azar denemeye çalıştım ama aşırı katı tip sistemi hoşuma gitmedi
Kod yazmaktan çok derleyiciyi memnun etmeye zaman harcadım. Clojure, Common Lisp ve C'de derleyiciyi ya da tip sistemini istediğim yöne bükme özgürlüğüm daha fazla
Elbette artıları ve eksileri var, ama kod yazarken engellenmek istemiyorum. Bu benim tarzım olabilir ama aynı şekilde düşünen çok kişiyle karşılaştım
Statik tip sisteminin bir tercih meselesi olduğuna inanıyorum; karşı taraf farklı düşünüyor. Yine de birbirine karşıt kamplar olması iyi, çünkü birbirlerinden çok şey öğreniyorlar
Rich Hickey, Clojure bir host dili olduğu için gerektiğinde Java ya da JavaScript'e inmenin sorun olmadığını birçok kez söyledi
Kişisel olarak bunun Clojure ekosisteminin en güçlü özelliklerinden biri olduğunu düşünüyorum. Çünkü çok daha pragmatik
“Şu pis başka dilde yazılmış kütüphane”ye dokunmak istemedikleri için aynı şeyi sevdikleri dilde tekrar tekrar yeniden gerçekleştirmeye çalışan çok girişim gördüm. Yavaş, güvensiz, esnek değil vb. gerekçeler çeşitli
Bu tür şeyler Common Lisp'te de, Rust topluluğunda da sık yaşandı ve hâlâ yaşanıyor
SBCL bu bildirimleri mümkün olduğunca garanti altına alarak muhtemelen her projede birkaç dakikalık baş ağrısından kurtarmıştır
Ayrıca CL çok pragmatik; örneğin woo'nun C kütüphanesi tabanlı olmasına kimse gözünü bile kırpmaz
Operasyonel sorunların hepsi küçük ve ince meselelerle ilgiliydi. Bir çalışma zamanı yükseltmesiyle JSON anahtar sırasının değişip JSON'u elle ayrıştıran eski bir istemciyi bozması ya da yığının derinlerindeki bir kütüphanenin HTTP kodunu string yerine integer olarak döndürmeye başlayıp hata ayrıştırmayı bozması gibi
Operasyon ölçeğinde müşterilerin küçük bir yüzdesi bile etkilense yüzlerce sorun raporu geldiği için her kesinti büyük bir olaydı
Şimdi küçük bir startup'ta çalışıyorum ve tamamen farklı. İlgi ve insan gücü o tarafa aktığı için statik bir dil seçtik, ama dinamik dillerle çalıştığım zamanları özlüyorum
Hızlı yayın yaptığımız için kod kalitesi gerçekten kötüleşiyor. Zaman baskısı nedeniyle tip sistemiyle uğraşıp temiz refactoring yapmak yerine, biraz farklı aynı nesne yapılarından birkaç tane kopyalayıp her yerde kullanıyoruz
Tip şekli yüzünden genelleme zor olduğundan, işlevi ayırıp birçok yerden referanslamak yerine aynı mantığı 10 yerde kopyala/yapıştır yapıyoruz
Problem alanına göre farklı çözümlerin doğru olduğunu düşünüyorum. İnce bug'lar yüzünden gece yarısı çağrılıyorsanız katı statik tipler muhtemelen doğrudur
Hızlı yineleme yapıyorsanız ve sorunlar tek bir e-posta ya da Slack mesajıyla çözülebiliyorsa dinamik tipler daha iyi olabilir
Püf noktası, büyüyüp statik paradigmaya geçmeniz gerektiğinde dinamik paradigmada çok fazla borç biriktirmemek
Ne yazık ki ateşli hayranları olan diller kolayca “başka dilin kütüphanelerini kullanmak istemiyorum” tavrına kayıyor gibi görünüyor
Clojure açık ara en sevdiğim dil ve bana doğal geliyor
Ancak diğer dillerden gerçekten özlediğim iki şey anlaşılır hata mesajları ve tip ipuçları
Şirkette büyük bir kod tabanını refactor etmem gerekti ve acı vericiydi. Hangi fonksiyonun exception fırlatacağını, hangisinin sadece nil döndüreceğini bilmek zor; çekirdek bir fonksiyonun vector mü, sequence mı yoksa başka bir collection mı döndüreceğini bilmek de zor
spec, malli, typed clojure gibi bunu çözmeye çalışan projeler var, ama Python ya da TypeScript'teki tiplere yakın bir alternatif gibi hissettirmediler
Fonksiyona neyin girip neyin çıktığını söyleyemeyen bir dil, birkaç dosyayı aşan kod tabanlarında neredeyse değersiz geliyor
Mevcut kodun bir nesne üzerinde yapabiliyor gibi göründüğü şeylere bakarak tipi çıkarsamaya çalışmak, insanın kendi kendine acı çektirmesi demek
Clojure ile ilk kez tanışıyorsanız şu interaktif öğreticiye bakmanız iyi olur: https://tryclojure.org
Kolay seviyeden orta ve zor seviyeye uzanan daha iyi bir interaktif öğrenme seti
Clojure eğlenceli
Rails 2010'ların web geliştirmesine hükmetti ve çok başarılı startup'ların büyük bir kısmı onun üzerine kuruldu
Mühendislerin “oyuncaklarla oynadığını” söyleyip alay eden çok kişi var, ama bugün sahip olduğumuz iyi şeylerin önemli bir kısmı bir zamanlar oyuncaktı. Linux da iyi bir örnek
Çok “eğlenceli” bir dilin zor yanı, her şeyi o dille çözebileceğinizi varsaymaya başlamanız
Sonunda çok kod yazıyorsunuz; oysa sonradan bakınca hiç kod yazmamak da yeterince iyi bir çözüm olabilirmiş
[1] https://franz.hamburg/writing/clojure-makes-happy.html