Algebraic Effects Neden Gereklidir?
(antelang.org)- 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
mapgibi 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 Failgibi 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
resumesı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
canile belirtilirsay_message: Unit -> Unitgibi bir efekt fonksiyonunu çağırmak, efekti “fırlatmak” gibi davranır- Çağıran fonksiyon,
foo () can SayMessagegibi bir imzayla bu efekti kullanabildiğini gösterir
handleifadesitry/catchbenzeri şekilde efekti yakalar veresumeçağrısıyla duran hesaplamayı sürdürürsay_messagehandler'ıprint "Hello World!"çalıştırıp ardındanresume ()çağırırsa özgün hesaplama devam eder ve42dö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
- generator'lar
- istisnalar
- async
- coroutine'ler
- otomatik türev alma
- Efekt polimorfizmi, what color is your function sorununu azaltır
map (input: Vec a) (f: a -> b can e): Vec b can e, girdi fonksiyonufhangieefektini gerçekleştirirsemap'in de aynı efekti gerçekleştirdiğini ifade eder- Aynı
map, stdout'a yazma, asenkron fonksiyon çağrıları veya streamyieldişlemleriyle birlikte kullanılabilir - Birçok effect handler dili efekt değişkeni
e'yi atlayarak alışıldıkmap (input: Vec a) (f: a -> b): Vec bbiçimini kullanabilir
- İstisnalar, efekt işlenirken
resumeçağrılmaması yoluyla uygulanabilirThrow aefekti içinthrow: a -> never_returnstanı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 aefektininyield: a -> Unitbiçimiyle uygulanabilir- Vektör elemanları dolaşılırken
yield elemçağrılır filterhandler'ı, yield edilen değer koşulu sağlıyorsa tekraryield xçağırır veresume ()ile sonraki elemana geçermy_for_eachhandler'ı, yield edilen her değer içinffonksiyonunu çalıştırır veresume ()ile devam eder
- Vektör elemanları dolaşılırken
- İşbirlikçi scheduler da
yield: Unit -> Unitefektiyle oluşturulabilir ve handler kontrolü alıp başka bir fonksiyonun çalışmasına geçebilir- Effekt'in scheduler örneği bu kalıbı gösterir
- 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
Databaseefekti 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 Databaseolur ve içeridequery "..."çağrılır
- Geleneksel biçimde
- 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_databasehandler'ıquerymesajını yok sayıp her zamanDbResponse.Okdöndürecek şekilderesumeedebilir
- Çıktı da efekt olarak ele alınırsa test sırasında stdout'a doğrudan yazmak yerine metin olarak toplanabilir
print_to_stringhandler'ıprint msgçağrılarını yakalar veall_messagesmetnine satır sonlarıyla ekleroutput_messages, gerçek çıktı olmadan dönüş değeri1234ile mesaj metnini doğrulayabilir
- Loglama,
Logefekti veLogLevelile koşullu çıktıya dönüştürülebilirlog_handler, mesaj seviyesi ayarlanan eşik düzeyinin üzerindeyseprint msgçağırırfoo () with log_handler Erroryalnı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 aefekti bir durum efekti olarak görülebilir veget: Unit -> a,set: a -> Unitsağlarstatehandler'ı başlangıç durumunu tutar;getiçin mevcut context'i döndürür,setiçinse yeni context ile günceller- Örnek
statetanımı sahiplik kurallarını yok sayar; gerçek uygulamadaCopy akı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,examplegibi fonksiyonlarınstringsargümanını sürekli alması gerekir - Efekt tabanlı uygulamada ilkel işlemler olan
push_stringveget_string,get/setçağırır; üst düzey kodunstrings'i doğrudan geçmesi gerekmez
- Efekt kullanılmazsa
- 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,
Prngnesnesini program boyunca doğrudan taşımanın yükünü gösterir- Global
Prngkullanışlıdır, ancak thread safety gereksinimi gibi global değerlere özgü dezavantajlar getirir Randomefektindekirandom: Unit -> U8kullanı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/urandomveya 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
- Global
- Bellek tahsisi de
Allocateefektiyle ifade edilebilirallocate: (size: Usz) -> Alignment -> Ptr afree: 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 tkullanıldığında başarı yolunuand_then,mapile 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, IOveparse (s: String): U32 can Fail, sıradan ardışık kod gibiline = ...,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",Failefektini 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.ErrorLibraryB.bar (): U32 can Throw LibraryB.Errormy_function,Throw LibraryA.Error,Throw LibraryB.Error,Throw MyErrorefektlerini birlikte tanımlayabilir- Liste uzarsa
AllErrors = can Throw ...gibi tür takma adları oluşturulabilir - Aynı
Throw Stringefekti tek bir efektte birleşir; ayırmak istenirseMyErrorgibi 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 IOgibi bir belirtim yoksa yan etkiler kullanılamaz externtanımları derleyici tarafından denetlenemez; bu yüzden tür tanımlarına güvenmek gerekir- Yalnızca debug modunda
IOefekti çalıştırıp release modunda efekt güvenliğini koruma yaklaşımı planlanan bir özelliktir
- Ante'de
- 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,
rrhata ayıklama aracına benzer bir kayıt/yeniden oynatma olanağı sağlayabilirrecordvereplayadlı iki handler,main'in dışa verdiği en üst seviye efekti, genellikleIO'yu işlerrecord, efekt oluşumlarını ve sonuçları kaydeder, sonra gerçek işleme için yerleşikIOhandler'ına yeniden iletirreplay, gerçekIOyapmaz; 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 gizliceIOyapmadığını gösterir- Kütüphane güncellemesinden sonra
get_pi: Unit -> F64 can IOhaline gelirse, çağıran taraftaki fonksiyon zatenIOistemiyorsa kod derleme hatası verir - Yalnızca en az gerekli efektleri tanımlamak tercih edilir; örneğin genel
IOyerine sadecePrinttanı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
IOgerektirmeye başlasa bile, onu çağıran fonksiyon zatenIO'ya izin veriyorsa hata çıkmayabilir Failefekti için de benzer durum geçerlidir; daha önce başarısız olmayan bir kütüphane fonksiyonu sonradanFailedebilir hale gelirse mevcutFailhandler'ı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
- Bir fonksiyon sonradan
- 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
- tail-resumptive efekt, handler'ın en sonunda
- 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.