Belki de monad vs istisnalar


Yanıtlar:


57

Temelde aynı şekilde çalışan ancak yerine rastgele bir değer döndürmenize izin veren Maybekuzenini kullanmak , istisnalardan biraz farklı bir amaca hizmet eder. Java açısından, bir çalışma zamanı istisnası yerine işaretli bir istisna olması gibi. Bir şeyler temsil beklenen sen daha çok beklemiyorduk bir hata daha uğraşmak zorunda.EitherNothing

Öyleyse, gibi bir işlev indexOfbir Maybedeğer döndürür çünkü öğenin listede olmaması ihtimalini beklersiniz. Bu, nullsizi nulldurumla başa çıkmaya zorlayan güvenli bir yöntem dışında, bir işlevden geri dönmeye benzer . Eitherhata durumuyla ilişkili bilgileri geri verebilmeniz dışında aynı şekilde çalışır, bu yüzden aslında bir istisna dışındadır Maybe.

Peki Maybe/ Eitheryaklaşımın avantajları nelerdir? Birincisi, dilin birinci sınıf vatandaşı. Bir işlev kullanarak Eitherbir istisna atan bir işlevi karşılaştıralım . İstisna durumu için tek gerçek başvurunuz bir try...catchifadedir. İçin Eitherfonksiyonu, sen akış kontrolü daha anlaşılır hale getirmek için mevcut combinators kullanabilirsiniz. Burada bir çift örnek var:

Öncelikle, üst üste hata yapabilecek birkaç işlevi denemek istediğinizi varsayalım. Herhangi bir hatasızlık alamazsanız, özel bir hata mesajı döndürmek istersiniz. Bu aslında çok kullanışlı bir kalıp ama kullanımı korkunç bir acı olurdu try...catch. Neyse ki, Eithernormal bir değer olduğu için kodu daha net hale getirmek için mevcut işlevleri kullanabilirsiniz:

firstThing <|> secondThing <|> throwError (SomeError "error message")

Başka bir örnek isteğe bağlı bir işleve sahip olmaktır. Diyelim ki, bir sorguyu optimize etmeye çalışan biri dahil olmak üzere, çalıştırmanız gereken çeşitli işlevleriniz var. Bu başarısız olursa, her şeyin başka şekilde çalıştırılmasını istersiniz. Gibi bir şey kod yazabilirsiniz:

do a <- getA
   b <- getB
   optional (optimize query)
   execute query a b

Bu davaların ikisi de kullanmaktan daha net ve kısa try..catchve daha da önemlisi daha anlamlıdır. Gibi bir işlev kullanmak <|>veya optionalniyetlerinizi, istisnaları her zaman işlemek için kullanmaktan daha açık bir şekilde yapar try...catch.

Ayrıca , kodunuzu bunun gibi satırlarla doldurmanız gerekmediğini unutmayın if a == Nothing then Nothing else ...! Tedavi Maybeve Eitherbir monad olarak tüm mesele, bundan kaçınmaktır. Yayılma anlamını bind fonksiyonuna kodlayabilirsiniz, böylece null / error kontrollerini ücretsiz alabilirsiniz. Eğer başka bir şey dönmek isterseniz açık bir şekilde kontrol etmek zorunda tek zaman Nothingverilen bir Nothingbu kod daha güzel hale getirmek için standart kütüphane fonksiyonları bir grup vardır: ve hatta o zaman kolaydır.

Son olarak, bir başka avantaj da Maybe/ Eithertipinin daha basit olmasıdır. Dili ek anahtar kelimeler veya kontrol yapılarıyla genişletmeye gerek yoktur - her şey sadece bir kütüphanedir. Bunlar sadece normal değerler olduğu için tip sistemini basitleştirir - Java'da, kullanmayacağınız türler (örneğin, geri dönüş tipi) ve efektler (örn. throwsİfadeler) arasında ayrım yapmanız gerekir Maybe. Aynı zamanda, diğer kullanıcı tanımlı türler gibi davranırlar - dile eklenmiş özel bir hata işleme koduna gerek yoktur.

Diğer bir galibiyet, functor ve monad'lar Maybe/ Eithervar olan, yani mevcut monad kontrol akış fonksiyonlarından (adil bir sayı olan) yararlanabilecekleri ve genel olarak diğer monadlarla birlikte iyi oynayabilecekleri anlamına geliyor.

Bu, bazı uyarılar olduğunu söyledi. Birincisi, kontrol edilmeyen istisnaları Maybene Eitherdeğiştirmez ne de değiştirir . Her bölmenin bir değer getirmesi çok acı verici olacağı için 0'a bölmek gibi şeylerle başa çıkmak için başka bir yol isteyeceksiniz .Maybe

Başka bir problem, birden fazla hata türünün geri dönüşüdür (bu sadece bunun için geçerlidir Either). İstisnalarla, aynı işlevde herhangi bir farklı istisna türünü atabilirsiniz. ile Either, sadece bir tür olsun. Alt yazarak ya da yapıcı olarak tüm farklı hata türlerini içeren bir ADT ile bunun üstesinden gelinebilir (bu ikinci yaklaşım genellikle Haskell'de kullanılan şeydir).

Yine de, hepsinden önemlisi Maybe/ Eitheryaklaşımını tercih ediyorum çünkü daha basit ve daha esnek buluyorum.


Çoğunlukla hemfikir, ancak "isteğe bağlı" örneğin anlamsız olduğunu sanmıyorum - bunu istisnalar dışında da yapabileceğiniz gibi görünüyor: void Optional (Action act) {try {act (); } catch (İstisna) {}}
05'te Errorsatz

11
  1. Bir istisna, sorunun kaynağı hakkında daha fazla bilgi taşıyabilir. OpenFile()Atar FileNotFoundveya NoPermissionveya TooManyDescriptorsvb. A Hiçbiri bu bilgiyi taşımaz.
  2. İstisnalar, geri dönüş değeri olmayan bağlamlarda kullanılabilir (örneğin, kendi dillerindeki kurucular ile).
  3. Bir istisna, bilgileri çok fazla if None return Nonestil ifadesi olmadan yığına kolayca göndermenizi sağlar .
  4. İstisna işleme hemen hemen her zaman sadece bir değer döndürmekten daha yüksek performans etkisi yaratır.
  5. Hepsinden önemlisi, bir istisna ve Belki de monad'in farklı amaçları vardır - bir problemi belirtmek için bir istisna kullanılır, ancak bir Belki değildir.

    "Hemşire, 5. odada bir hasta varsa , beklemesini isteyebilir misin?"

    • Belki monad: "Doktor, 5 numaralı odada hasta yok."
    • İstisna: "Doktor, 5 numaralı oda yok!"

    ("eğer" a dikkat edin - bu, doktorun Belki bir monad beklediği anlamına gelir )


7
2. ve 3. maddelere katılmıyorum. Özellikle, 2 aslında monad kullanan dillerde bir sorun teşkil etmiyor. Çünkü bu dillerin inşaat sırasında başarısızlıklarla başa çıkma zorunluluğunu oluşturmayan sözleşmeleri var. 3 ile ilgili olarak monads ile dilleri yok şeffaf bir şekilde monads işlemek için uygun sözdizimine sahiptir değil açık kontrol gerektirirler (örneğin, Nonedeğerler sadece yayılan olabilir). 5. puanınız tek tür haktır… soru şu: hangi durumlar açıkça istisnai? Görünüşe göre, çok değil .
Konrad Rudolph

3
Bununla birlikte (2), bindtest etmenin Nonesözdizimsel bir ek yüke neden olmayacak şekilde yazabilirsiniz . Çok basit bir örnek, C # Nullableoperatörleri uygun şekilde aşırı yüklüyor. NoneTürü kullanırken bile kontrol etmek gerekmez. Tabii ki kontrol hala yapılır (güvenlidir), fakat perde arkasında ve kodunuzu karıştırmaz. Aynısı bir anlamda (5) 'e itiraz etmemize itirazınız için de geçerlidir, ancak bunun her zaman geçerli olmayabileceği konusunda hemfikirim.
Konrad Rudolph

4
@Oak: (3) ile ilgili Maybeolarak, bir monad olarak muamele etmenin bütün amacı yayılan Noneörtük yapmaktır . Bu, Noneverilen geri dönmek istiyorsanız None, hiçbir özel kod yazmanıza gerek olmadığı anlamına gelir . Eşleşmen gereken tek zaman, özel bir şey yapmak istersen None. Asla bir if None then Noneçeşit ifadeye ihtiyacın yok .
Tikhon Jelvis

5
@Oak: Bu doğru, ama bu gerçekten benim amacım değildi. Demek istediğim, nulltam olarak böyle (örneğin if Nothing then Nothing) ücretsiz kontrol edersiniz çünkü Maybebir monad İçin bind ( >>=) tanımında kodlanmıştır Maybe.
Tikhon Jelvis

2
Ayrıca, (1) ile ilgili olarak: kolayca Eithergibi davranan hata bilgilerini (örneğin ) taşıyabilecek bir monad yazabilirsiniz Maybe. İkisi arasında geçiş yapmak aslında oldukça basittir çünkü Maybegerçekten özel bir durumdur Either. (In Haskell, düşünebildiğim Maybeolarak Either ().)
Tikhon Jelvis

6

"Belki" istisnaların yerine geçmez. İstisnaların istisnai durumlarda kullanılması amaçlanmıştır (örneğin: bir db bağlantısı açılması ve db sunucusu olması gerektiği halde orada değildir). "Belki", geçerli bir değere sahip olabileceğiniz veya bulunmayacağınız bir durumu modellemek içindir; bir anahtar için bir sözlükten bir değer elde edersiniz: orada olabilir veya olmayabilir - bu sonuçların herhangi biri hakkında "istisnai" bir şey yoktur.


2
Aslında, eksik bir anahtarın bir noktada bazı şeylerin çok yanlış gittiği, büyük olasılıkla kodumda veya girişi o noktada kullanılan herhangi bir şeye dönüştüren kodda bir hata olduğu anlamına gelen sözlükleri içeren oldukça fazla kodum var.

3
@delnan: Kayıp bir değerin istisnai bir durumun işareti olamayacağını söylemiyorum - olması gerekmiyor. Belki de bir değişkenin geçerli bir değere sahip olabileceği ya da olmayabileceği bir durumu gerçekten modeller - C # 'da null değerlerini düşünün.
Nemanja Trifunoviç

6

Tikhon'un cevabını ikinci olarak söyledim, ancak herkesin kaçırdığı çok önemli bir pratik nokta olduğunu düşünüyorum:

  1. İstisna işleme mekanizmaları, en azından ana dillerde, ayrı ayrı konulara çok yakından bağlıdır.
  2. EitherMekanizma hiç vida dişlerine bağlı değildir.

Bugünlerde gerçek hayatta gördüğümüz bir şey, birçok zaman uyumsuz programlama çözümünün Eitherhata işleme tarzının bir türünü benimsemesidir . Bu bağlantıların herhangi birinde ayrıntılı olarak açıklandığı gibi Javascript vaatlerini düşünün :

Vaat kavramı, bunun gibi asenkron bir kod yazmanıza izin verir (son bağlantıdan alınmıştır):

var greetingPromise = sayHello();
greetingPromise
    .then(addExclamation)
    .then(function (greeting) {
        console.log(greeting);    // 'hello world!!!!’
    }, function(error) {
        console.error('uh oh: ', error);   // 'uh oh: something bad happened’
    });

Temel olarak, söz bir nesnedir:

  1. Henüz bitmemiş ya da bitmemiş bir zaman uyumsuz hesaplamanın sonucunu temsil eder;
  2. Sonuçta gerçekleştirmek için daha fazla işlem zincirlemenizi sağlar; bu sonuç elde edildiğinde tetiklenir ve sonuçları söz olarak verilir;
  3. Sözün hesaplanması başarısız olursa çağrılacak bir başarısızlık işleyicisi bağlamanıza olanak tanır. İşleyici yoksa, hata zincirdeki sonraki işleyicilere yayılır.

Temel olarak, dilin yerel istisna desteği, hesabınız birden fazla iş parçacığında gerçekleştiğinde işe yaramadığından, söz verilen bir uygulamanın bir hata işleme mekanizması sağlaması gerekir ve bunlar Haskell'in Maybe/ Eithertürlerine benzer monadlar olarak ortaya çıkar .


Konu ile ilgisi yok. Tarayıcıda JavaScript, her zaman birden fazla iş parçacığında değil bir iş parçacığında çalışır. Ancak yine de istisna kullanamazsınız çünkü gelecekte işleve ne zaman çağrılacağını bilmiyorsunuz. Asenkron, otomatik olarak ipliklerin katılımı anlamına gelmez. İstisnalarla çalışamamanızın da nedeni budur. Bir özel durum çağırırsanız yalnızca bir istisna alabilirsiniz ve hemen çalıştırılır. Fakat Asenkron'in tüm amacı, gelecekte başka bir şey bittiğinde ve hemen değil, gelecekte devam etmesidir. Bu yüzden orada istisnalar kullanamazsınız.
David Raab,

1

Haskell tip sistemi, kullanıcının a olasılığını kabul etmesini gerektirirken, Nothingprogramlama dilleri çoğu zaman bir istisnanın yakalanmasını gerektirmez. Bu, derleme sırasında kullanıcının bir hata kontrolü yaptığını bileceğimiz anlamına gelir.


1
Java'da işaretli istisnalar var. Ve insanlar genellikle onlara bile kötü davrandılar.
Vladimir

1
@ DairT'arg ButJava, GÇ hatalarını (örnek olarak) kontrol eder, ancak NPE'ler için kontrol etmez. Haskell'de bunun tam tersi geçerli: null eşdeğeri (Belki) işaretli, ancak GÇ hataları basitçe (denetlenmeyen) istisnalar atıyor. Ne kadar fark yaratacağından emin değilim ama Haskellers'in Belki'den şikayet ettiğini duymuyorum ve bir çok insanın NPE'lerin kontrol edilmesini istediğini düşünüyorum.

1
@delnan İnsanlar NPE'nin kontrol edilmesini istiyor mu? Kızgın olmalılar. Ve bunu kastettim. NPE'yi denetlemek, yalnızca ilk etapta kaçınmanın bir yolunu varsa (Java'nın sahip olmadığı null olmayan referanslar alarak) anlamlıdır .
Konrad Rudolph

5
@KonradRudolph Evet, insanlar muhtemelen throws NPEher imzaya ve catch(...) {throw ...} her tek yöntem gövdesine bir ekleme yapmak zorunda kalacakları bakımından kontrol edilmesini istemezler . Ancak belki de aynı şekilde kontrol edilmek üzere bir pazar olduğuna inanıyorum.

1
@delnan Ah, o zaman katılıyorum.
Konrad Rudolph

0

Belki de monad temelde çoğu ana dilin "boş araç hatası" kontrolünü kullanmasıyla aynıdır (boş olanı kontrol etmesini gerektirmez hariç) ve büyük ölçüde aynı avantaj ve dezavantajlara sahiptir.


8
Doğru kullanıldığında statik olarak kontrol edilebildiğinden, aynı dezavantajlara sahip değildir . Belki monad'ları kullanırken boş gösterici istisnası eşdeğeri yoktur (yine doğru kullanıldığı varsayılarak).
Konrad Rudolph

Aslında. Bunun nasıl netleştirilmesi gerektiğini düşünüyorsun?
Telastyn

@Konrad Bunun nedeni, Belki'deki değeri (muhtemelen) kullanmak için, Yok'u kontrol etmeniz gerekir (elbette bu kontrolün çoğunu otomatikleştirme yeteneği de vardır). Diğer diller bu tür bir el kitabını tutar (çoğunlukla inanıyorum felsefi nedenlerle).
Donal Fellows

2
@Donal Evet, ancak monadları uygulayan dillerin çoğunun uygun sözdizimsel şeker sağladığını ve böylece bu kontrolün tamamen gizlenebileceğini unutmayın. Örneğin , kontrol etmeye gerek kalmadanMaybe yazarak sadece iki sayı ekleyebilirsiniz ve sonuç bir kez daha isteğe bağlı bir değerdir. a + b None
Konrad Rudolph

2
Null türleri ile karşılaştırılması için de geçerlidir Maybe tip , ancak kullanarak Maybebir monad olarak çok daha zarif boş-imsi mantığını ifade verir sözdizimi şeker ekler.
tdammers

0

İstisna işleme faktoring ve test için gerçek bir acı olabilir. Python'un katı "try ... catch" bloğu olmadan istisnaları yakalamanıza izin veren "with" sözdizimi sağladığını biliyorum. Fakat Java'da, örneğin, catch catch blokları büyük, tek parça, ayrıntılı veya son derece ayrıntılı ve kırılması zor. Bunun üzerine, Java, kontrol edilen ve kontrol edilmeyen istisnalar etrafındaki tüm gürültüyü ekler.

Bunun yerine, monadınız istisnalar yakalar ve onları monadik alanın bir özelliği olarak görür (bazı işlem anomalileri yerine), o zaman ne attığınıza veya yakaladığınıza bakmaksızın o alana bağladığınız işlevleri karıştırma ve eşleştirmede serbestsiniz.

Eğer, daha iyisi, monadınız istisnaların olabileceği koşulları önlerse (boş bir Çek'i Belki'ye itmek gibi), sonra daha da iyi. eğer ... o zaman faktörü denemek ve denemek çok ... çok daha kolaydır.

Gördüğüm kadarıyla Go, her bir işlevin döneceğini (cevap, hata) belirterek benzer bir yaklaşım izliyor. Bu, işlevi, çekirdek yanıt türünün bir hata bildirimi ve etkin bir şekilde yan basma fırlatma ve yakalama istisnaları ile süslendiği bir monad boşluğuna "kaldırma" ile aynıdır.

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.