Haskell'deki hataları bildirmenin en temiz yolu


22

Haskell öğrenmek için çalışıyorum ve yazdığım fonksiyonlarda hatalarla başa çıkmak için üç farklı yolla karşılaştım:

  1. error "Some error message."Bir istisna atan basitçe yazabilirim .
  2. İşlev geri dönüşümü Maybe SomeTypeyapabilirim, geri dönmek istediğim şeyi geri verebilirim veya geri verebilirim.
  3. İşlevime geri döndürebilirim Either String SomeType, burada bir hata mesajı veya ilk önce ne döndürmem istendi.

Sorum şu: Hangi hatalarla mücadele yöntemini kullanmalıyım ve neden? Belki bağlama göre farklı yöntemler kullanmalıyım?

Şu anki anlayışım:

  • Tamamen işlevsel koddaki istisnalar ile uğraşmak "zor" ve Haskell'de ise olayları olabildiğince işlevsel tutmak istiyor.
  • Döndürme Maybe SomeType, işlev başarısız olursa veya başarılı olursa (örneğin, başarısız olabileceği farklı yollar yoktur) yapılacak doğru şeydir .
  • Either String SomeTypeBir işlevin çeşitli yollardan biriyle başarısız olması durumunda, geri dönüş yapılması gereken doğru şeydir.

Yanıtlar:


32

Tamam, Haskell'de ilk hata işleme kuralı: Hiçbir zaman kullanmayınerror .

Her şekilde sadece korkunç. Tamamen bir tarih eylemi olarak var olur ve Prelude'un onu kullanması çok korkunç. Kullanmayın.

Kullanabileceğiniz akla gelebilecek tek zaman, bir şeyin içsel olarak korkunç olduğu ve gerçekliğin dokusunda bir şeylerin yanlış olması gerektiği, dolayısıyla programınızın sonucunu ortaya çıkarması gerektiğidir.

Şimdi soru Maybevs olur Either. bir değere dönüşebilecek ya da vermeyecek bir Maybeşey için çok uygundur head, ancak başarısız olmanın tek bir nedeni vardır. Nothing"kırdı ve nedenini zaten biliyorsun" gibi bir şey söylüyor. Bazıları kısmi bir işlevi gösterdiğini söyleyebilirdi.

En sağlam hata işleme biçimi Either+ bir hata ADT'sidir.

Mesela hobi derleyicilerimden birinde gibi bir şeyim var

data CompilerError = ParserError ParserError
                   | TCError TCError
                   ...
                   | ImpossibleError String

data ParserError = ParserError (Int, Int) String
data TCError = CouldntUnify Ty Ty
             | MissingDefinition Name
             | InfiniteType Ty
             ...

type ErrorM m = ExceptT CompilerError m -- from MTL

Şimdi bir grup hata türünü tanımlayıp iç içe geçiririm, böylece görkemli bir üst düzey hatam olur. Bu derleme herhangi bir aşamasında bir hata veya ImpossibleErrorbir derleyici hata belirten bir olabilir.

Bu hata türlerinin her biri, güzel baskı veya diğer analizler için mümkün olduğunca çok bilgi tutmaya çalışır. Daha da önemlisi, bir dize sahibi olmadan, yazım denetleyicisiyle yazılmamış bir program çalıştırmanın aslında bir birleştirme hatası oluşturduğunu test edebilirim! Bir şey bir kez Stringolursa, sonsuza dek gider ve içerdiği herhangi bir bilgi derleyiciye / testlere opaktır, bu yüzden de Either Stringpek iyi değildir.

Sonunda bu türü ExceptTMTL'den yeni bir monad transformatörü olarak paketledim. Bu esasen EitherThataların saf ve hoş bir şekilde atılması ve yakalanması için güzel bir fonksiyon dizisiyle birlikte gelir.

Son olarak, Haskell'in, içinde bir istisna yakalamak dışında, diğer diller gibi istisnaları ele alma mekanizmalarına sahip olduğunu belirtmekte fayda var IO. Bazı insanların bunları IOher şeyin potansiyel olarak başarısız olabileceği ağır uygulamalar için kullanmaktan hoşlandıklarını biliyorum , ancak çok nadiren bunun hakkında düşünmekten hoşlanmıyorlar. Bu saf istisnaları mı yoksa sadece ExceptT Error IObir zevk meselesi mi kullandığınız . Şahsen tercih ediyorum ExceptTçünkü başarısızlık şansını hatırlatmak hoşuma gidiyor.


Özet olarak,

  • Maybe - Belli bir şekilde başarısız olabilirim
  • Either CustomType - Başarısız olabilirim ve sana ne olduğunu anlatacağım.
  • IO+ istisnalar - Bazen başarısız oluyorum. Ne zaman fırlattığımı görmek için doktorlarımı kontrol et
  • error - Ben de senden nefret ediyorum kullanıcı

Bu cevap benim için çok şey temizliyor. Kendimi, sanki yerleşik işlevlerin kullandığı headveya lastkullandığı göründüğü gerçeğine dayanarak ikinci kez tahmin errorediyordum, bu yüzden bunun aslında bir şeyleri yapmanın iyi bir yolu olup olmadığını merak ediyordum ve sadece bir şeyleri özlüyorum. Bu soruya cevap veriyor. :)
CmdrMoozy 11:14

5
@CmdrMoozy Yardımcı olmaktan mutluluk duyuyorum :) Başlangıçta böyle kötü uygulamalar olduğu için talihsiz bir durum. Bu şekilde miras yolu: /
Daniel Gratzer 11:14


2

2 ve 3'ü bir şeyin kaç şekilde başarısız olabileceğine dayanarak ayırmam. Tek bir olası yol olduğunu düşündüğünüz anda başka biri yüzünü gösterecektir. Bunun yerine metrik "arayanlarım neden bir şeyin başarısız olduğunu önemsiyor mu? Aslında bu konuda herhangi bir şey yapabilir mi?"

Bunun ötesinde, benim Either String SomeTypeiçin bir hata durumu doğurabilecek hemen belli değil . Daha açıklayıcı bir isimle koşulları olan basit bir cebirsel veri tipini yapardım.

Hangi kullandığınız, karşılaştığınız sorunun niteliğine ve birlikte çalıştığınız yazılım paketinin deyimlerine bağlıdır. Yine de 1 numaradan kaçınmaya meyilliyim.


Aslında, Eitherhataları kullanmak Haskell'de çok iyi bilinen bir kalıptır.
Rufflewind

1
@ rufflewind - Eitherbelirsiz kısım değil, Stringfiili bir dizgeden ziyade bir hata olarak kullanılması .
Telastyn,

Ah, niyetini yanlış anladım.
Rufflewind
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.