3 puan yazan GN⁺ 2025-05-25 | Henüz yorum yok. | WhatsApp'ta paylaş
  • Algebraic effects, yeniden başlatılabilir istisnalar gibi kontrol akışını yakalayıp işleyen bir dil özelliğidir; Ante'nin temel özelliklerinden biridir ve Koka, Effekt, Eff, Flix gibi araştırma dillerinde de merkezi olarak kullanılır
  • Aynı mekanizma ile generator'lar, istisnalar, async, coroutine'ler, otomatik türev alma kütüphane düzeyinde oluşturulabilir ve efekt polimorfizmi sayesinde map gibi fonksiyonlar da efekt türünden bağımsız olarak yalnızca bir kez yazılabilir
  • Veritabanı erişimi, çıktı, loglama, durum aktarma gibi bağımlılık enjeksiyonu ve context aktarımı efektlere dönüştürülürse test mock'ları, çıktı toplama ve log filtreleme handler değişimiyle yönetilebilir
  • Fonksiyon imzalarında can IO, can Print, can Fail gibi efektler görünür olduğunda saflık garantisi, kayıt/yeniden oynatma ve güvenlik denetimi açısından avantaj sağlar; ancak zaten izin verilmiş efektler istemeden mevcut handler'a yayılabilir
  • Geleneksel zayıf nokta verimlilik kaygısı idi; ancak son dönemdeki diller tail-resumptive efekt optimizasyonu, evidence passing, tek resume sınırı ve handler özelleştirmesiyle maliyeti azaltıyor

Algebraic effects'in temel modeli

  • Algebraic effects, effect handlers olarak da adlandırılır ve “yeniden başlatılabilir istisnalar” modeliyle anlaşılabilir
  • Ante sözde kodunda efekt fonksiyonları tanımlanır ve fonksiyon imzasında ilgili efektin kullanılabileceği can ile belirtilir
    • say_message: Unit -> Unit gibi bir efekt fonksiyonunu çağırmak, efekti “fırlatmak” gibi davranır
    • Çağıran fonksiyon, foo () can SayMessage gibi bir imzayla bu efekti kullanabildiğini gösterir
  • handle ifadesi try/catch benzeri şekilde efekti yakalar ve resume çağrısıyla duran hesaplamayı sürdürür
    • say_message handler'ı print "Hello World!" çalıştırıp ardından resume () çağırırsa özgün hesaplama devam eder ve 42 döndürür
  • “algebraic” adı büyük ölçüde tarihsel bir terimdir; pratikte effect handlers daha doğru bir ifadedir, ancak kullanıcılara tanıdık geldiği için algebraic effects adı kullanılmaktadır

Kullanıcı tanımlı kontrol akışı

  • Algebraic effects, çeşitli dil özelliklerinin tek bir mekanizma ile uygulanmasını sağlar
  • Efekt polimorfizmi, what color is your function sorununu azaltır
    • map (input: Vec a) (f: a -> b can e): Vec b can e, girdi fonksiyonu f hangi e efektini gerçekleştirirse map'in de aynı efekti gerçekleştirdiğini ifade eder
    • Aynı map, stdout'a yazma, asenkron fonksiyon çağrıları veya stream yield işlemleriyle birlikte kullanılabilir
    • Birçok effect handler dili efekt değişkeni e'yi atlayarak alışıldık map (input: Vec a) (f: a -> b): Vec b biçimini kullanabilir
  • İstisnalar, efekt işlenirken resume çağrılmaması yoluyla uygulanabilir
    • Throw a efekti için throw: a -> never_returns tanımlanır
    • Sıfıra bölme durumunda throw "error: Division by zero!" çağrılır ve handler mesajı yazdırdıktan sonra hesaplamayı yeniden başlatmaz
  • Generator'lar, Yield a efektinin yield: a -> Unit biçimiyle uygulanabilir
    • Vektör elemanları dolaşılırken yield elem çağrılır
    • filter handler'ı, yield edilen değer koşulu sağlıyorsa tekrar yield x çağırır ve resume () ile sonraki elemana geçer
    • my_for_each handler'ı, yield edilen her değer için f fonksiyonunu çalıştırır ve resume () ile devam eder
  • İşbirlikçi scheduler da yield: Unit -> Unit efektiyle oluşturulabilir ve handler kontrolü alıp başka bir fonksiyonun çalışmasına geçebilir
  • Birden fazla efekt birbiriyle iyi şekilde birleşir; bu da diğer efekt soyutlamalarına kıyasla kullanım kolaylığı sağlayan bir avantaj olarak görülür

Bağımlılık enjeksiyonu ve test edilebilirlik

  • Efektler, genel iş uygulamalarında da bağımlılık enjeksiyonu için kullanılabilir
  • Veritabanı nesnesini doğrudan fonksiyon argümanı olarak geçirmek yerine Database efekti tanımlanabilir
    • Geleneksel biçimde business_logic (db: Database) (x: I32) veritabanı nesnesini argüman olarak alır
    • Efekt tabanlı biçimde bu, business_logic (x: I32) can Database olur ve içeride query "..." çağrılır
  • Hangi somut veritabanının kullanılacağına çağrı yığınının üst kısmındaki handler karar verir
    • Üretim veritabanı başka bir veritabanıyla değiştirilebilir veya test için mock veritabanı kullanılabilir
    • mock_database handler'ı query mesajını yok sayıp her zaman DbResponse.Ok döndürecek şekilde resume edebilir
  • Çıktı da efekt olarak ele alınırsa test sırasında stdout'a doğrudan yazmak yerine metin olarak toplanabilir
    • print_to_string handler'ı print msg çağrılarını yakalar ve all_messages metnine satır sonlarıyla ekler
    • output_messages, gerçek çıktı olmadan dönüş değeri 1234 ile mesaj metnini doğrulayabilir
  • Loglama, Log efekti ve LogLevel ile koşullu çıktıya dönüştürülebilir
    • log_handler, mesaj seviyesi ayarlanan eşik düzeyinin üzerindeyse print msg çağırır
    • foo () with log_handler Error yalnızca hata loglarını yazdırır

Daha temiz API'ler ve context aktarımı

  • Algebraic effects, program veya kütüphane boyunca taşınan Context nesnesi kalıbını efektlerle ifade edebilir
  • Use a efekti bir durum efekti olarak görülebilir ve get: Unit -> a, set: a -> Unit sağlar
    • state handler'ı başlangıç durumunu tutar; get için mevcut context'i döndürür, set içinse yeni context ile günceller
    • Örnek state tanımı sahiplik kurallarını yok sayar; gerçek uygulamada Copy a kısıtı gerekebilir
  • Vektör içinde metin saklayıp indeksleri anahtar gibi iletme örneği, context aktarım maliyetini gösterir
    • Efekt kullanılmazsa push_string, get_string, append_with_separator, example gibi fonksiyonların strings argümanını sürekli alması gerekir
    • Efekt tabanlı uygulamada ilkel işlemler olan push_string ve get_string, get/set çağırır; üst düzey kodun strings'i doğrudan geçmesi gerekmez
  • Bu yaklaşım, iç context aktarımının kütüphane tarafından kapsüllendiği durumlarda iyi çalışır
    • Kütüphane kullanıcılarının context aktarımının iç ayrıntılarıyla ilgilenmesine gerek kalmaz
    • Belirli bir context türüne bağlanmamak için gereken fonksiyonlar bir arayüzle soyutlanabilir

Global değişkenlerin yerine ve direct style

  • Rastgele sayı üretimi veya bellek tahsisi gibi dışarıdan bakıldığında durumsuz görünen ama gerçekte durum gerektiren API'ler, global değişkenler yerine efektlerle ifade edilebilir
  • Rastgele sayı üretimi örneği, Prng nesnesini program boyunca doğrudan taşımanın yükünü gösterir
    • Global Prng kullanışlıdır, ancak thread safety gereksinimi gibi global değerlere özgü dezavantajlar getirir
    • Random efektindeki random: Unit -> U8 kullanılırsa kullanıcı yalnızca üst çağrı yığınında bir yerde handler ile başlatmayı açıkça belirtir
    • Sonrasında /dev/urandom veya başka bir rastgelelik kaynağına geçmek için yalnızca handler'ı değiştirmek yeterlidir; çağrı yığınının kalan kodunun değişmesi gerekmez
  • Bellek tahsisi de Allocate efektiyle ifade edilebilir
    • allocate: (size: Usz) -> Alignment -> Ptr a
    • free: Ptr a -> Unit
    • Çoğu çağrı global allocator kullanabilir; ancak sıkı döngülerde döngü gövdesine bir handler eklenerek arena allocator'a geçilebilir
  • Efektler, sonuçları özel sarmalayıcı değerlerle taşımak yerine direct style yazımı mümkün kılar
    • Maybe t kullanıldığında başarı yolunu and_then, map ile zincirlemek gerekir
    • Rust'taki ? gibi sözdizimsel şekerler, iyi yola odaklanmayı kolaylaştırmak içindir
    • Efekt tabanlı get_line_from_stdin (): String can Fail, IO ve parse (s: String): U32 can Fail, sıradan ardışık kod gibi line = ..., x = ..., x * 2 şeklinde yazılabilir
  • Hata işleme, iyi yoldan sapıldığında handler uygulanması şeklinde ele alınabilir
    • get_line_from_stdin () with default "42", Fail efektini bir varsayılan değerle işler
  • Farklı hata türleri de efekt listeleriyle doğal biçimde birleşir
    • LibraryA.foo (): U32 can Throw LibraryA.Error
    • LibraryB.bar (): U32 can Throw LibraryB.Error
    • my_function, Throw LibraryA.Error, Throw LibraryB.Error, Throw MyError efektlerini birlikte tanımlayabilir
    • Liste uzarsa AllErrors = can Throw ... gibi tür takma adları oluşturulabilir
    • Aynı Throw String efekti tek bir efektte birleşir; ayırmak istenirse MyError gibi bir sarmalayıcı tür gerekir

Saflık, yeniden çalıştırılabilirlik ve güvenlik denetimi

  • Çoğu effect handler dili, OCaml gibi bazı istisnalar dışında, yan etkilerin oluşabileceği yerlerde efektleri kullanır
    • Ante'de can Print, can IO gibi bir belirtim yoksa yan etkiler kullanılamaz
    • extern tanımları derleyici tarafından denetlenemez; bu yüzden tür tanımlarına güvenmek gerekir
    • Yalnızca debug modunda IO efekti çalıştırıp release modunda efekt güvenliğini koruma yaklaşımı planlanan bir özelliktir
  • Bazı fonksiyonlar girdi olarak saf fonksiyonlar ister
    • Thread oluşturulurken, oluşturulan thread'in mevcut thread'e ait handler'larla çağrı yapamaması gerekir
    • spawn_all (functions: Vec (Unit -> a pure)): Vec a can IO, yalnızca saf fonksiyonları alır, hepsini thread olarak çalıştırır ve tamamlanmalarını bekler
  • Software Transactional Memory (STM), saf fonksiyon gerektiren bir eşzamanlılık tekniğidir
    • Birden fazla fonksiyon eşzamanlı çalıştırılırken işlem sırasında bir değer başka bir thread tarafından değiştirilirse ilgili transaction yeniden başlatılır
    • Effekt'in kavram kanıtlama uygulaması effekt-stm içinde yer alır
  • Saflık, rr hata ayıklama aracına benzer bir kayıt/yeniden oynatma olanağı sağlayabilir
    • record ve replay adlı iki handler, main'in dışa verdiği en üst seviye efekti, genellikle IO'yu işler
    • record, efekt oluşumlarını ve sonuçları kaydeder, sonra gerçek işleme için yerleşik IO handler'ına yeniden iletir
    • replay, gerçek IO yapmaz; efekt günlüğündeki sonuçları kullanır
    • Debug build'de varsayılan olarak kayıt yapılırsa deterministik hata ayıklama elde edilebilir
  • Fonksiyon imzalarındaki efekt listesi, Capability Based Security yaklaşımına benzer biçimde güvenlik denetimine yardımcı olur
    • get_pi: Unit -> F64, arka planda gizlice IO yapmadığını gösterir
    • Kütüphane güncellemesinden sonra get_pi: Unit -> F64 can IO haline gelirse, çağıran taraftaki fonksiyon zaten IO istemiyorsa kod derleme hatası verir
    • Yalnızca en az gerekli efektleri tanımlamak tercih edilir; örneğin genel IO yerine sadece Print tanımlamak daha iyidir
    • Yeni efekt eklemek semantic versioning'i bozan bir değişiklik olarak değerlendirilir
    • İlgili kaynaklar arasında Capability Based Security ve Designing with Static Capabilities and Effects bulunur

Sınırlar ve uygulama stratejileri

  • Efekt yaklaşımının sınırlarından biri, istenmeyen yakalamanın mümkün olmasıdır
    • Bir fonksiyon sonradan IO gerektirmeye başlasa bile, onu çağıran fonksiyon zaten IO'ya izin veriyorsa hata çıkmayabilir
    • Fail efekti için de benzer durum geçerlidir; daha önce başarısız olmayan bir kütüphane fonksiyonu sonradan Fail edebilir hale gelirse mevcut Fail handler'ına yayılabilir
    • Bu davranış bazı durumlarda kabul edilebilir olsa da, örneğin varsayılan değer verme gibi özel bir işleme isteniyorsa amaçla uyuşmayabilir
  • Geleneksel başlıca dezavantaj verimlilik kaygısı olsa da, son dönemde efektlerin derleme çıktısı önemli ölçüde iyileşti
  • Birçok algebraic effects dili, tail-resumptive efektleri sıradan closure çağrıları olarak optimize eder
    • tail-resumptive efekt, handler'ın en sonunda resume çağırdığı efekt türüdür
    • Gerçek dünyadaki efektlerin çoğu bu sınıfa girer ve metindeki örneklerin büyük kısmı da bu kategoridedir
    • İstisnalar ise hiç resume çağırmadıkları için istisnai durum olarak sınıflanır
  • Dillere göre optimizasyon stratejileri de farklıdır
    • Koka, evidence passing kullanır ve efektleri handler'a kadar yukarı taşıyarak çalışma zamanı olmadan C'ye derler
    • Ante ve OCaml, resume'ün en fazla bir kez çağrılmasına izin verir
      • Bu kısıt, belirsiz seçim gibi bazı efektleri dışarıda bırakır
      • Buna karşılık kaynak yönetimini basitleştirir ve segmented stacks benzeri yöntemlerle iç continuation'ların daha verimli uygulanmasını sağlar
    • Effekt, handler'ları program içinde tamamen özelleştirip ortadan kaldırır
      • Bu yaklaşım, çoğu fonksiyonu second-class hale getiren bir kısıt getirir
      • boxed biçimle first-class fonksiyonlar elde edilebilir ve pay-as-you-go yaklaşımına geçilebilir
      • İlgili kaynaklar arasında Effekt captures dokümantasyonu ve makale yer alır

Henüz yorum yok.

Henüz yorum yok.