Elixir'e Geçiş
(leemeichin.com)- Bir Ruby backend geliştiricisi, Elixir’i ana dili olarak kullanan bir şirkete katıldıktan sonra, Ruby’den etkilenmiş söz dizimi sayesinde Elixir/Erlang deneyimi olmasa da nispeten hızlı uyum sağladı
- Elixir, Ruby gibi söz dizimsel eğlence ve DSL dostu bir yapı sunarken; sınıf, instance ve kalıtım içermeyen değişmez fonksiyonel modeliyle durum yönetimi yükünü azaltır
- Ruby DSL’leri çalışma zamanında nesneleri ve metotları dinamik olarak tanımlamaya dayanırken, Elixir DSL’leri derleme zamanı makrolarıyla çalışma zamanı kodu üretir ve hataları daha erken ortaya çıkarır
- Pipeline söz dizimi,
withifadesi ve{:ok, result}biçimindeki Result tuple’ları, normal akış ile kurtarılabilir hata işlemeyi ayırmak için kullanışlıdır - Ruby’de exception’lara,
nil/falsedeğerlerine ve nesne içi hata durumlarına dayanan yaklaşıma kıyasla Elixir’in açıkok/errordönüşlerinin daha tatmin edici olduğu; 3 aylık kullanımdan sonra bile yazma deneyiminin keyifli kaldığı düşünülüyor
Ruby deneyimi olan birinin Elixir’e uyum sağlama biçimi
- Yeni iş yerinde backend’in ana dili olarak Elixir kullanılıyor; başlamadan önce Elixir veya Erlang kodu yazma deneyimi yoktu
- Erlang, Prolog ile biraz uğraşmış olma deneyimi nedeniyle yabancı gelmedi; Elixir ise Ruby’den güçlü biçimde etkilenmiş söz dizimi sayesinde hızlı öğrenilebildi
- Henüz en iyi uygulamalar, mimari ve daha düşük seviyeli Erlang kavramlarında uzman olunmasa da, bu noktalar Elixir’e girişin önünde engel olmadı
- Elixir, Ruby’nin verdiği programlama keyfini korurken OOP dillerinde yaygın olan durumla ilgili tuzakları azaltan bir dil olarak benimsendi
- Sınıf, instance ve kalıtım yoktur
- Değişmezlik ve fonksiyonel stil üzerine kuruludur
- Statik tip sistemine bağlı değildir
- Değişebilir duruma sahip kodun bakımı çok daha zordur; durumun fonksiyon yerelinde mi, instance’ta mı yoksa sınıfta mı olduğu her zaman açık değildir
- Ruby’de her şey nesne olduğu için her nesne durum taşıyabilir; sezgisel DSL’ler oluşturma sürecinde fonksiyon, instance ve sınıf seviyesindeki durumların karışması kolaydır
- Ruby’nin Eigenclass kavramı, sınıfın sınıfı gibi düşünülebilir; bir instance’ın eigenclass’ı değiştirmesi diğer instance’ların durumunu da etkileyebilir
DSL ve derleme zamanı makroları arasındaki fark
- Elixir de Ruby gibi DSL’leri destekler ve söz dizimi tanıdıktır; ancak temel fark DSL’in ne zaman oluşturulduğundadır
- Elixir DSL’leri derleme zamanı makrolarıdır ve çalışma zamanı kodu üretir
- Ruby DSL’leri çalışma zamanını değiştirerek nesneleri ve metotları dinamik olarak tanımlama yöntemine dayanır
- Elixir makroları yanlış kullanılırsa kütüphanede hata oluşur; üretilen makro kodu yanlışsa derleyici hatası ortaya çıkar
- Bu tür hatalar derleme zamanında göründüğü için testler gerçek uygulama mantığına daha fazla odaklanabilir
Pipeline ve with ile oluşan akış
- Elixir’in pipeline operatörü
|>kullanışlıdır, ancak Clojure’un threading operatörlerinin de avantajları vardır- Clojure’un
->operatörü, Elixir’in|>operatörü gibi önceki ifadenin sonucunu sonraki fonksiyonun ilk argümanı olarak geçirir - Clojure’un
->>operatörü, önceki sonucu sonraki fonksiyonun son argümanı olarak geçirir - Bu fark küçük görünse de anonim fonksiyon kullanmadan birlikte çalışabilir kod yazarken kullanışlıdır
- Clojure’un
- Elixir’in
withifadesi, Haskell ve Lisp’tekiletifadesini çağrıştırırken yerleşik birotherwisevarmış gibi davranır- Karmaşık fonksiyonlar, üstte normal yol ve altta hata işleme olacak şekilde bölünebilir
{:ok, result}ile pattern matching yapılamazsa bu, kurtarılabilir hata olarak ele alınabilir; bu yüzden Result tuple’larıyla iyi uyum sağlar
Açık hata dönüşlerinin sağladığı avantaj
- Ruby’de kontrol akışı için exception kullanımı yaygındır; açık hata tipleri yerine çoğu zaman başarılı sonuç,
nil,falseveya nesne içi hata durumuna dayanılır- Örneğin
model.update(params)başarısız olduğundafalsedöndürür ve başarısızlık nedeninimodel.errorsiçinde saklar - Bu da yeniden değişebilir durum sorununa yol açar
- Örneğin
Result/Eithermonad’larında olduğu gibiok/errorveyaleft/rightdeğerlerini dönüş değeri olarak açıkça belirtme yaklaşımı, Ruby tarzı exception akışına göre daha tatmin edici hissettirir- Elixir’i 3 ay kullandıktan sonra bile Elixir, Ruby’nin gösterdiği “programlama eğlencelidir” hissini sürdüren bir dil olarak kalıyor
1 yorum
Hacker News yorumları
Son birkaç haftadır neredeyse her gün Elixir YouTube videoları izliyorum
Kısa süre önce bir Elixir konferansı olmuş gibi; birkaçını izledikten sonra YouTube sürekli Elixir içerikleri önermeye başladı
Erlang’ın fikirlerini seviyorum ama eskiden ejabberd sohbet sunucusu gibi şeylerle uğraşırken bana fazla tuhaf gelmişti
Joe Armstrong videolarını her izlediğimde onun değeri bilinmemiş bir dâhi olduğunu düşünüyorum; izole süreçler ve mesaj iletimi, yani aktör modeli, dağıtık programlamanın geleceği için iyi bir aday gibi görünüyor
Crockford’un yeni Misty dili videosunda https://www.youtube.com/watch?v=R2idkNdKqpQ da soruların Elixir/Erlang tarafına kaymış olması etkileyiciydi
Yine de LiveView ve LiveBook’un “sihri”ne bakınca henüz %100 ikna olmuş değilim. Fazla iyi görünen şeylerde genelde bir tuzak çıkmıştır; GenServer gibi yabancı terimler ve platforma özgü bilgi de tek yönlü bir portaldan içeri giriyormuş hissi veriyor, başka yerlere taşınabilir bilgi olup olmadığından emin değilim
Go’nun da oldukça iyi bir eşzamanlılık hikâyesi varken, küçük topluluğa sahip bir dil ve platform; işe alım, kütüphaneler ve kaynaklar açısından büyük risk taşıyor. Yine de Elixir topluluğunun başardıkları gerçekten etkileyici; tip sisteminin nasıl uygulanacağını da merakla bekliyorum
sendin nasıl çalıştığına bakmak yeterliGenServer, sayısız kez elle yazacağın bir örüntünün doğal biçimde genelleştirilmiş hâli; aktör bilgisi de başka yerlere taşınabilir
Go’da da aktör soyutlamasını uygulayan kütüphaneler var; süreç posta kutusu da Go’nun kanalları gibi bir mesaj kuyruğu olarak görülebilir. Fark daha çok yorumlayıcının ve işlemciyi devretmenin nasıl çalıştığı tarafında
Öte yandan LiveView’un sihir gibi görünmesi normal. Gerçekten de çok sayıda hareketli parça birbirine geçiyor ve birçok topluluğun son 10 yılda biriktirdiği çalışmaların sonucu; soyutlama çok olsa da fikrin kendisi olgun
Riak gibi şeyler zamanının ilerisindeydi ve GenServer yapar gibi sağlam dağıtık sistemler kurabileceğin fikrine neredeyse sahipti
Hem Chris hem José, erken Elixir çevresinin dışında görünürlüğü artırmak için çok çaba harcıyor; topluluk içindeki insanlar da kendi seviyelerinde aynı şeyi yapıyor
İşe alım sanıldığı kadar karmaşık değil. Bu dille çalışmak isteyen epey kişi var ve iyi geliştiriciler oldukça hızlı yetişiyor
Yine de niş dil olma tarafını evcilleştirmek gerekiyor. Bu, dilin kendisi için bir risk ama kısa vadede niş bir teknolojide yetkin olmanın ödülünü de getirebiliyor
Çoğu şeyin ne yaptığını görüp anlayabiliyor ve kodu okuyabiliyorsun. JavaScript’te çoğu zaman durum böyle değil
İşe almanın ve öğretici içerik bulmanın zor olduğu doğru. Örneğin Elixir’de yapmak istediğim şeyi daha önce yapmış birini bulmak JavaScript’e kıyasla çok daha zor
Kütüphaneler büyük bir sorun olmadı; oldukça çok var. API’lerle çalışırken JavaScript veya Python için istemci kütüphaneleri olup Elixir için olmadığı durumlar oluyor; ama genelde yalnızca bir HTTP istemcisiyle API kullanmak çok da zor değil
Şu anda Node.js kullanmam istememden değil, yapmak zorunda olmamdan. Denedikten sonra yeni bir projeye Node.js ile asla başlamayacağımı düşündüm
Node.js’in eşzamanlılık kontrolü, ölçeklenebilirlik, güvenilirlik ve gözlemlenebilirlik araçları BEAM ekosisteminin çok gerisinde
İnsanların Elixir’i inanması zor derecede iyi bulmasının nedeni, bazen dayanıklılık için tasarlanmamış platformlara fazlasıyla alışmış olmaları bence
LiveBook’ta fark hesaplama veya veri sıkıştırmanın tam ayrıntıları dışında doğru zihinsel modeli edinmek oldukça kolay. Sıkışırsan WebSocket mesajlarına bakarak da neler olduğunu epey anlayabiliyorsun
LiveView şaşırtıcı derecede sezgisel
Elixir/Erlang’ın büyük cazibesi, web sunucusu bağlamında arka plan işlerini gerçekten kolay çalıştırabilmek ve bloklayan G/Ç’nin tüm sunucuyu durdurmasından endişe etmemek
Önceki işimde bir webhook işleyicisinin içinde çok sayıda HTTP isteği yapmam gerekiyordu; aynı anda yeterince çok gerçekleştiğinde işletim sistemi süreçlerinin hepsi meşgul oluyor ve tüm site çöküyordu
Böyle zamanlarda “Elixir olsaydı bir Task başlatıp devam ederdim” diye içten içe çok düşünmüştüm
Tokio async süreçleri arasında nesne paylaşma fikri, Elixir’in birinci sınıf özelliklerine kıyasla zor hissettirdi. İmkânsız ya da doğası gereği zor demiyorum ama doğru yapmak daha zor görünüyordu
Arka plan işlerini HTTP isteklerinden farklı bir hesaplama kapasitesi üzerinde tutmak isterim. Çünkü kaynak kullanım örüntüleri farklı; yeniden deneme, hata işleme ve backpressure’ı netleştirmek için öne bir durum veya kuyruk koymak isterim
Elbette bu karmaşıklık olmasa ve aynı framework her şeyi halletse güzel olurdu; ama yalnızca programlama dili ya da runtime ile oraya kadar gidilebileceğini sanmıyorum. İzleme ve operasyon prosedürleri de çözümün parçası olmalı
Sasa Juric’in The Soul of Erlang and Elixir sunumu, bu dilin ne kadar güçlü olabileceğini iyi gösteriyor
https://www.youtube.com/watch?v=JvBT4XBdoUE&t=4
“Bırak çöksün” ya da “BEAM eşzamanlılığı doğru yapıyor” sözlerini duyup hâlâ kafasında oturtamayanlara şiddetle öneririm. Gerçekten harika bir sunum
1991'den beri her gün Erlang ile programlama yapıyordum ve böyle devam etmeyi düşünüyordum.
Bu süre içinde Haskell, Rust, Elixir gibi dilleri de denedim. Güçlü tipler çok değerli, ama sonunda mesele en çok nerede keyif aldığınıza geliyor.
Erlang bana en büyük keyfi veren programlama dili ve birkaç yıl sonra emekli olana kadar en azından Erlang üzerinde hacklemeye devam etmeyi planlıyorum.
Elixir'i tanıyalı bir yılı biraz geçti; öğrenmek ve çeşitli bileşenlere yerleştirip denemek oldukça keyifliydi.
En büyük keyif, yalnızca BEAM/OTP ile yerleşik olarak gelen çok şey olmasından kaynaklanıyor.
Birlikte çalıştığım ekipten biri bir keresinde “BEAM/OTP, karmaşık kısımları çıkarılmış k8s gibi” demişti.
Birkaç yıl Elixir öğrendim, ama yakınlarda tasarladığım OpenShift, yani k8s geçişi yüzünden bu avantaj bir ölçüde seyreldi.
k8s olmasaydı çok daha cazip bir öneri olurdu. Onun yerine mevcut geliştirme yığınını koruduk ve OTP ile yapacağımız şeyi k8s kavramlarıyla başardık. Elbette trade-off'lar farklıydı.
“Elixir, Ruby gibi eğlenceli bir dil verirken nesne yönelimli dillerin durumla ilgili mayınlarını ortadan kaldırıyor. Sınıf yok, instance yok, kalıtım yok; değişmez ve fonksiyonel, üstelik statik tip sistemine de bağlı değil.”
Bunu istiyorum ama tipler de olsun isterdim. Büyük kod tabanlarında güçlü tiplerin doğru olduğuna eminim. Çünkü pek çok sihri görünür kılıyor ve akıl yürütmeyi kolaylaştırıyor.
Fonksiyonların pattern matching/unification özelliği sayesinde verinin gidip gelirken hangi biçimde olduğunu epey iyi anlayabiliyorsunuz. Yine de içiniz rahat etmiyorsa Dialyzer var.
Dialyzer, Erlang/Elixir için statik analiz aracıdır. Standart Erlang dağıtımına dahildir ve “DIscrepancy AnaLYZer for ERlang programs” ifadesinin kısaltmasıdır.
Tek bir Erlang modülünde ya da uygulama paketlerinde tip hataları, ölü kod, gereksiz testler gibi tutarsızlıkları bulur. Doğru programları temel alarak tipleri çıkarır; tip anotasyonları zorunlu değildir, ama varsa daha iyi uyarılar sağlar.
https://fly.io/phoenix-files/adding-dialyzer-without-the-pai...
https://elixir-lang.org/blog/2023/06/22/type-system-updates-...
GenServer nesneye yakındır; sadece ilişkili fonksiyonları olan bir struct değil, mesajlar üzerinden etkileşime girer.
Projedeki GenServer'lar için ortak kayıt, bellek ayarları, logging gibi temel bir şablon koyup bunu üç farklı varyasyondaki belirli işlere göre özelleştirebilmek isterdim.
Elixir bu sorunu makro kod üretimiyle kapatıyor ama pek iyi değil. Benim GenServer'larım önemsiz tekrarlarla dolu.
“Ruby'de kontrol akışı için exception kullanmak yaygındır” sözü bence düpedüz yanlış.
O paragrafın ardından gelen örnek Rails'in
updatemetodu, oysa standart Rails örneklerinde ve generator'larında kullanılan biçim exception kullanmayanupdatesürümüdür.Belirli bir ölçeğe ulaşmış kütüphane veya uygulamalar yeterince karmaşıklaştığında açıklayıcı hata sınıflarına geçer. Çünkü ActiveRecord'daki gibi durum tabanlı hata işleme acı vericidir.
Ruby'de artık bir ölçüde pattern matching olduğuna göre, kontrol akışı için exception kullanımının yerini alabileceğini düşünüyorum. Exception fırlatmak yerine sınıfın kendisini döndürüp onunla eşleştirme yapabilirsiniz.
Pek çok gem kontrol akışı için exception kullanıyor; yazdığım veya bakımını yaptığım uygulama ve kütüphanelerde de bu yaygındı.
Rails'te hata durumunda exception fırlatan çok sayıda
bang!metodu var. Ancak genel olarak Ruby'de, Python'da olduğu gibi, kontrol akışı için exception kullanmak gerçekten yaygın.F#’tan geldim ve Elixir’i sadece biraz kurcaladım; dil ne yazık ki biraz kafa karıştırıcı
Örneğin fonksiyon imzası yalnızca adı ve parametre sayısını gösteriyor, tipleri göstermiyor; bu yüzden büyük kütüphanelerde neyin nereye girdiğini zihinde kavramak epey zor oluyor
Yanlış parametre verdiğinizi de çoğu zaman ancak kodu çalıştırıp hata ayıkladıktan sonra görüyorsunuz. F# gibi katı tipli dillerde bu tür sorunlar kod yazarken hemen yakalandığı için zaman ve emek ciddi ölçüde azalıyor
Bu yüzden Erlang ortamı dışında Elixir’in başlıca cazibesinin ne olduğunu merak ediyorum
https://elixir-lang.org/blog/2023/09/20/strong-arrows-gradua...
Şu anda dinamik bir dil olduğu doğru; bu nedenle dilin kendisinden çok fazla tip bilgisi alamıyorsunuz. Henüz statik tipli bir dil değil
Ancak kullandığım diğer dillere kıyasla Elixir’e üstünlük kazandıran şey gerçekten BEAM/OTP
Deneyimime göre başka dillerde kütüphane olarak getirmeniz gereken birçok özellik varsayılan olarak geliyor. Bu tür kütüphaneler karmaşıklığı ciddi biçimde artırıyor ve bazen öğrenme eğrisi de yüksek oluyor. Asenkron ile ilgili özellikler buna iyi bir örnek
Elixir’i bir süre kullanıp bu yerleşik araçları ve soyutlamaları öğrendiğinizde tüm ekosistemin gücünü anladığınızı düşünüyorum
Ana cazibesi muhtemelen Ruby hissi veren bir sözdizimiyle BEAM üzerinde çalışması. BEAM’in oldukça büyük bir avantaj olduğunu düşünüyorum
F#’ın tipleriyle pek karşılaştırılamaz ama typespec kullanılabiliyor: https://hexdocs.pm/elixir/1.15.7/typespecs.html
Örneğin Axon, Nx, BumbleBee ile makine öğreniminin yerel olarak entegre olduğu ve GPU derlemesine kadar giden bir akış var: https://www.youtube.com/watch?v=HK38-HIK6NA
Topluluk bu alanı gerçekten güçlü biçimde ileri taşıyor
LiveView’in çalışma biçimi de çekici. Gerçi .NET’te Blazor var
Elixir, gömülü sistemlerde Nerves veya betik yazımı gibi çeşitli bağlamlarda kullanılabiliyor. Bildiğim kadarıyla F# da betik yazımı için kullanılabiliyor
Genel olarak BEAM ve ilgili yapılar güçlü yönleri; paket yöneticisi Hex de gerçekten iyi. Elbette .NET’in araçları da iyi
typespec, pattern matching ve guard’larla idare edebilirsiniz ama epey zahmetli; en azından topluluk tarafından geliştirilen JetBrains eklentisinde IDE/IntelliSense dostu değil
Başlarken değil, 2 yıl kullandıktan sonra yazmak daha iyi olurdu
Yeni şeyler başta her zaman kusursuz, parlak, her derde deva çözümler gibi görünür
İlk izlenimlerin de kendine göre bir yeri var; benim sunduğum da bu. Hedef kitle Elixir/Erlang uzmanları değil, Ruby uzmanları
Elixir o zaman da şimdi de bir mücevher gibi. Mükemmel değil ama şaşırtıcı derecede iyi dayanıyor
2007 civarından beri Ruby kullanıyorum
Bir sonraki “havalı” şeye geçmeye her çalıştığımda sonunda Ruby’ye döndüm; çünkü Ruby’yi seviyorum, Ruby ile en hızlı çalışabiliyorum ve mesele bundan ibaretti
Ama son zamanlarda Elixir hakkında çok daha fazla şey görüyorum; dış görünüşü ve hissi hoşuma gidiyor. Yakında yeni bir yan proje başlatacağım, sanırım bir denemem gerekecek