Kurulum
Zero'da başarısızlık ayrı, paralel bir kontrol akışı değildir. Düzenli kontrol akışının parçasıdır, fonksiyonun imzasında bildirilir ve her çağrı yerinde kabul edilir. İki parça bunu mümkün kılar:
- Fonksiyon imzasında
raises— "bu fonksiyon başarısız olabilir." - Çağrı yerinde
check— "bu başarısız olursa, fonksiyonumu aynı error ile başarısız yap."
Bu kombinasyon, çoğu dilin try/catch ya da Result tiplerine başvurduğu şeyi ifade etmeye yeter.
Başarısızlığa Açık Bir Fonksiyon Tanımlamak
Dönüş tipinden sonra raises ekleyin:
fun validate(ok: Bool) -> i32 raises { InvalidInput } {
if ok == false {
raise InvalidInput
}
return 42
}
İmzayı okumanın iki yolu:
- "
validateya biri32döner ya daInvalidInputraise eder." - "Olası sonuç kümesi {
i32,InvalidInput}'dir."
Her iki okuma da doğrudur. Compiler her iki olasılığı da takip eder ve çağıranların her biri hakkında explicit bir şey yapmasını ister.
Çıplak bir raises clause'u da yazabilirsiniz:
pub fun main(world: World) -> Void raises {
check world.out.write("hello\n")
}
raises (error listesi yok), "bu fonksiyon herhangi bir error ile başarısız olabilir" anlamına gelir. main üzerinde bu, geleneksel formdur — bir şeyler ters giderse program nonzero status ile çıkabilir ve runtime hatayı yüzeye çıkarmayı halleder.
Çağrı yığınının daha derinindeki fonksiyonlar için, başarısızlık modlarının her seviyede belgelenmesi için explicit form raises { ErrorA, ErrorB }'i tercih edin.
Bir Error Raise Etmek
Başarısızlığa açık bir fonksiyonun içinde raise, fonksiyondan verilen error ile çıkar:
fun validate(ok: Bool) -> i32 raises { InvalidInput } {
if ok == false {
raise InvalidInput
}
return 42
}
raise InvalidInput, o satırda bir InvalidInput error'ı üretir. Fonksiyon o noktadan ileri gitmez — kontrol çağırana döner ve çağıran bir i32 yerine error'ı görür. raises clause'u, bu fonksiyonun raise etmesine izin verilen tek error tiplerini listeler; listede olmayan bir şeyi raise etmek bir compile error'dır.
check ile Bir Error'ı Yansıtmak
Başarısızlığa açık bir fonksiyonu çağıran biri, olası başarısızlığı kabul etmek zorundadır. En yaygın kabul check'tir:
fun run() -> Void raises { InvalidInput } {
check validate(true)
}
check validate(true) iki şey yapar:
validate(true)'yu çağırır.validatebir error raise ettiyse onu yukarı yansıtır —runaynı error'ı kendi çağıranına raise eder.
Yansıtmaya izin verilmesi için, run'ın kendi raises clause'unda InvalidInput (veya uyumlu bir şey) raise edebileceğini bildirmesi gerekir. Compiler bunu kontrol eder. run'ın imzası raises { OtherError } demiş olsaydı, InvalidInput küme içinde olmadığı için yansıtma derlenemezdi.
Dilin resmi örneklerinden tam işlenmiş bir örnek — yansıtmanın başarılı olduğunu görmek için Run'a tıklayın:
Error tipi fonksiyon imzasıyla birlikte çağrı yığınında en yukarıya kadar gider. main'in çıplak raises'i run'ın raise edebileceği her şeyi kabul eder, böylece yansıtma güvenle düşer.
Neden Try/Catch Değil?
raises/check'in arkasındaki tasarım disiplini şudur: başarısızlık çağrı yerinde asla görünmez değildir. Bir try/catch dilinde, bir exception ona dahil olabileceğinden habersiz bir fonksiyonun içinden sessizce hareket edebilir — fonksiyon pure görünür, ama gövdesinin bir yerinde derinlerde bir çağrı throw eder ve exception oradan unwind eder.
Bu, throw eden kodun yazarı için kolaydır. Herkes için maliyetlidir:
- Okuyucular (ve ajanlar) bir imzadan bir fonksiyonun başarısızlık yollarına katılıp katılmadığını söyleyemez.
- Refactor etmek tetikte hale gelir — bir çağrıyı fonksiyonlar arasında taşımak hangi exception'ların ulaşılabilir olduğunu değiştirebilir.
- Recovery kodu, ne yapılacağını bilen yerden uzakta yaşar.
Zero bedeli önden öder — başarısızlığa açık her fonksiyonda annotation ve başarısızlığa açık her çağrıda check — böylece "bir fonksiyonun katıldığı başarısızlık modları imzasından görülebilir" özelliğini elde eder. Bu, hem insanların hem de ajanların güvenebileceği bir özelliktir.
Neden Sadece Result<T, E> Değil?
Aynı şeyi bir choice ile ifade edebilirsiniz — ok ve err variant'larına sahip bir Result<T, E> tipi. Zero size o deseni de verir; başarısızlık inceleyebileceğiniz, saklayabileceğiniz veya etrafa geçirebileceğiniz veri olduğunda faydalı bir araçtır.
raises/check'in eklediği, yaygın case için söz dizimi seviyesinde bir konvansiyondur: "bu başarısız olursa, fonksiyonumu aynı şekilde başarısız yap." Onsuz, her çağrı neredeyse her zaman error'ı çağıranın kendi Result'ına yeniden paketleyen bir match ile sarılırdı. check bunun için bir kısayoldur ve compiler yansıtmanın iyi tiplendirilmiş olmasını sağlar.
Yani:
Result<T, E>(bir choice) — başarısızlığı bir değer olarak inceleyip taşımak istediğinizde.raises+check— başarısızlığı yalnızca çağrı yığınında yukarı yansıtmak istediğinizde.
İkisi de mevcut; farklı ergonomik ihtiyaçları karşılarlar.
Birden Fazla Error Tipi
Bir fonksiyon birden fazla tip error raise edebilir:
fun parse(input: String) -> i32 raises { Empty, Malformed } {
if std.mem.len(input) == 0 {
raise Empty
}
// ... parse mantığı ...
raise Malformed
}
Bir çağıran şunları yapabilir:
- Kendi imzası hem
EmptyhemMalformed'ı (ya da bir superset'i) listeliyorsacheck parse(input)ile yansıtmak. - Dilin amaç için sunduğu
matchveyatrytarzı yapılarla bir veya her iki error'ı explicit ele almak.
İnce taneli ele alma için (spesifik error tipleri üzerinde match yapma ile diğerlerini yansıtma) kesin söz dizimi pre-1.0 Zero'da hareket edebilecek yüzeylerden biridir. Sözleşme — bir fonksiyonun raise edebileceği her error tipi imzasındadır — kararlı kısımdır.
Zihinsel Model
Zero'nun başarısızlık sistemi, her imperative dilin sahip olduğu şeyin sıkı-dürüst bir versiyonudur:
| Kavram | Try/Catch | Zero |
|---|---|---|
| Bir fonksiyonu başarısızlığa açık olarak işaretle | hiçbir şey (implicit) | raises { ... } |
| Bir error raise et | throw e | raise E |
| Çağırana yansıt | görünmeden kabarır | check call(...) |
| Yerel olarak ele al | try { ... } catch(e) { ... } | dönen bir Result üzerinde match ya da tipli bir handling formu |
Davranışsal fark küçük. Annotation farkı büyük — ve bilinçli. Effect'ler explicit'tir. Başarısızlıklar da effect'tir.
Stil Notları
check'i bol bol kullanın. Bu katmanda yapılacak belirli bir şeyiniz yoksa doğru varsayılan budur.- İç yardımcılarda çıplak
raises'ten kaçının. Error kümesi ne kadar dar olursa imza o kadar faydalıdır. - Başarısızlığa açık işlemleri ihtiyaç duydukları capability'lerle eşleştirin. Stdout'a yazan bir
World-kullanan fonksiyon neredeyse her zamanraisesister çünkü yazma işlemleri başarısız olabilir.
Sırada: JSON Diagnostics
raises/check, Zero'nun compiler diagnostic'leriyle yan yana çalışır — doğru error'ları bildirmeyen bir fonksiyona karşı check yazdığınızda, compiler size yapılandırılmış bir biçimde tam olarak neyin yanlış olduğunu söyler. Bir sonraki yazı JSON Diagnostics — bir ajanın kodu onarmak için okuduğu makine tarafından okunabilir feed.
Sıkça Sorulan Sorular
Zero'da raises ne anlama gelir?
raises ne anlama gelir?Bir fonksiyon imzasındaki raises, fonksiyonun başarısız olabileceğini bildirir. Çıplak bir raises herhangi bir error tipine izin verir. raises { InvalidInput } gibi belirli bir form, fonksiyonu yalnızca listelenmiş error tipleriyle başarısız olacak şekilde sınırlar. Çağıranlar başarısızlık olasılığını kabul etmek zorundadır — ya check ile ya da başka bir explicit handling formuyla.
check operatörü ne yapar?
check operatörü ne yapar?check expr, expr'i değerlendirir ve bir error üretirse o error'ı mevcut fonksiyonun çağıranına yansıtır. 'Bunu çalıştır, başarısız olursa çağıranımı aynı error ile başarısız yap' olarak düşünün. Yansıtmaya izin verilmesi için çağıranın raises clause'unun uyumlu bir error raise edebildiğini bildirmesi gerekir.
Zero'da bir error nasıl raise edilir?
raises clause'u o error'ı içeren bir fonksiyonun içinde raise ErrorName kullanın. Örnek: if ok == false { raise InvalidInput }. Fonksiyon o noktada çıkar; error fonksiyonun sonucu olur ve çağıran ona karşı check yapabilir.
Zero neden try/catch kullanmıyor?
Try/catch, exception'ların bunlardan haberdar olmayan fonksiyonların içinden sessizce hareket etmesine olanak verir. Zero'nun tasarımı, her fonksiyonun imzasının dahil olduğu başarısızlık modlarını kabul etmesi gerektiği yönündedir. Gizli kontrol akışı yok — bir fonksiyon başarısız olabiliyorsa imzası bunu söyler ve her çağıran bunu explicit olarak check ile kabul etmek zorundadır.
Bir fonksiyon Zero'da birden fazla error tipi raise edebilir mi?
Evet — raises { ... } clause'unda virgülle ayrılarak (veya mevcut Zero error set söz dizimini takip ederek) listelenirler. Olası error'ların kümesi, parametre ve dönüş tipi gibi fonksiyonun sözleşmesinin parçasıdır. Çağıranlar hangi error'ın raise edildiğine göre pattern match yapabilir veya check ile yansıtabilir.