例外に対するMaybe
monad の利点は何ですか? Maybe
はtry..catch
構文の明示的な(そしてスペースを消費する)方法のようです。
updateHaskellについては意図的にではないことに注意してください。
Maybe
(またはその従兄弟のEither
は基本的に同じように機能しますが、Nothing
の代わりに任意の値を返すことができます)を使用すると、例外とは少し目的が異なります。 Java用語では、実行時の例外ではなく、チェックされた例外があるようなものです。これは、何かを表しますexpected対処しなければならないエラーではなく、対処する必要があります期待する。
そのため、indexOf
のような関数は、Maybe
値を返します。これは、アイテムがリストにない可能性があるためです。これは、null
の場合に対処する必要があるタイプセーフな方法を除いて、関数からnull
を返すのとよく似ています。 Either
は同じ方法で機能しますが、エラーケースに関連する情報を返すことができるため、実際にはMaybe
よりも例外に似ています。
では、Maybe
/Either
アプローチの利点は何ですか?一つには、それは言語のファーストクラスの市民です。 Either
を使用する関数を、例外をスローする関数と比較してみましょう。例外的なケースでは、あなたの唯一の本当の手段はtry...catch
ステートメントです。 Either
関数の場合、既存のコンビネーターを使用してフロー制御をより明確にすることができます。次に例をいくつか示します。
最初に、エラーにならない関数を取得するまで、連続してエラーになる可能性のあるいくつかの関数を試したいとしましょう。エラーが発生しない場合は、特別なエラーメッセージを返します。これは実際には非常に便利なパターンですが、try...catch
を使用すると恐ろしい痛みになります。幸い、Either
は通常の値なので、既存の関数を使用してコードをより明確にすることができます。
firstThing <|> secondThing <|> throwError (SomeError "error message")
別の例としては、オプション機能があります。クエリを最適化しようとする機能など、実行する機能がいくつかあるとします。これが失敗した場合は、他のすべてをとにかく実行する必要があります。次のようなコードを書くことができます:
do a <- getA
b <- getB
optional (optimize query)
execute query a b
これらのケースはどちらもtry..catch
を使用するよりも明確で短く、さらに重要なことに、セマンティックです。 <|>
やoptional
などの関数を使用すると、try...catch
を使用して常に例外を処理するよりもmuchの意図が明確になります。
また、しないでくださいは、if a == Nothing then Nothing else ...
! Maybe
とEither
をモナドとして扱うことの要点はこれを避けることです。伝搬セマンティクスをbind関数にエンコードして、null /エラーチェックを無料で取得できます。明示的にチェックする必要があるのは、Nothing
を指定してNothing
以外のものを返したい場合だけです。それでも簡単です。そのコードをより美しくするための標準ライブラリ関数がたくさんあります。
最後に、もう1つの利点は、Maybe
/Either
型の方が単純であることです。追加のキーワードや制御構造で言語を拡張する必要はありません。すべてが単なるライブラリです。それらは単なる通常の値なので、型システムをより単純にします-Javaでは、throws
を使用しない場合の型(戻り値の型など)と効果(Maybe
ステートメントなど)を区別する必要があります。また、他のユーザー定義型と同じように動作します。言語に特別なエラー処理コードを組み込む必要はありません。
もう1つの勝利は、Maybe
/Either
がファンクターとモナドであることです。つまり、既存のモナド制御フロー関数(かなりの数があります)を利用でき、一般に、他のモナドとうまく連動します。
とはいえ、いくつかの注意点があります。まず、Maybe
もEither
もnchecked例外を置き換えません。 0で除算するなど、別の方法で処理する必要があります。これは、すべての除算でMaybe
の値を返すのは面倒だからです。
別の問題は、複数のタイプのエラーが返されることです(これはEither
にのみ適用されます)。例外を使用すると、同じ関数でさまざまなタイプの例外をスローできます。 Either
を使用すると、1つのタイプしか取得できません。これは、サブタイピングまたはコンストラクターとしてすべての異なるタイプのエラーを含むADTで克服できます(この2番目のアプローチは、Haskellで通常使用されるものです)。
それでも、全体的に、Maybe
/Either
のアプローチの方が簡単で柔軟性があるため、私はそれを好みます。
OpenFile()
はFileNotFound
またはNoPermission
またはTooManyDescriptors
などをスローできます。Aにはこの情報は含まれません。if None return None
スタイルのステートメントを使用しなくても、非常に簡単にスタックに情報を送信できます。最も重要なのは、例外とMaybeモナドの目的が異なることです。例外は問題を示すために使用されますが、Maybeはそうではありません。
「看護師、if 5部屋に患者がいます。待つように頼むことができますか?」
(「if」に注意してください-これは、医師が期待している多分モナドであることを意味します)
私はTikhonの答えに2番目ですが、誰もが欠けている非常に重要な実用的なポイントがあると思います。
Either
メカニズムは、スレッドにまったく結合されていません。ですから、今日私たちが実際に目にしているのは、多くの非同期プログラミングソリューションがEither
スタイルのエラー処理のバリアントを採用しているということです。これらのリンクのいずれかに詳述されているように、Javascript約束を検討してください:
Promiseの概念では、次のような非同期コードを記述できます(最後のリンクから取得)。
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’
});
基本的に、promiseは次のようなオブジェクトです。
基本的に、計算が複数のスレッドで発生している場合、言語のネイティブ例外サポートは機能しないため、promise実装はエラー処理メカニズムを提供する必要があり、これらはHaskellのMaybe
/と同様のモナドであることが判明します。 Either
タイプ。
「たぶん」は例外の代わりではありません。例外は、例外的なケースで使用することを目的としています(たとえば、db接続を開いているときに、dbサーバーがそこにないはずです)。 「たぶん」は、有効な値がある場合とない場合の状況をモデル化するためのものです。キーのディクショナリから値を取得しているとしましょう。それはある場合もない場合もあります。これらの結果のいずれにも「例外」はありません。
Haskell型システムでは、ユーザーがNothing
の可能性を認識する必要がありますが、プログラミング言語では、例外をキャッチする必要がないことがよくあります。つまり、コンパイル時に、ユーザーがエラーをチェックしたことがわかります。
例外処理は、因数分解とテストにとって非常に困難な場合があります。私は知っていますpythonは、厳密な "try ... catch"ブロックなしで例外をトラップできる素敵な "with"構文を提供します。しかし、たとえばJavaでは、try catchブロックは大きく、定型的です、冗長または非常に冗長で、分割するのは難しいです。さらに、Javaは、チェックされた例外とチェックされていない例外の周囲にすべてのノイズを追加します。
代わりに、モナドが例外をキャッチし、それらをモナド空間のプロパティとして処理する場合(処理の異常ではなく)、スローまたはキャッチするものに関係なく、そのスペースにバインドする関数を自由に組み合わせて使用できます。
さらに良いことに、モナドが例外が発生する可能性のある状態(NullチェックをMaybeにプッシュするなど)を防ぐ場合は、さらに良いでしょう。もし...それなら、試して...キャッチするよりもはるかに簡単に因数分解してテストすることができます。
Goは、各関数が(answer、error)を返すように指定することで、同様のアプローチをとっています。これは、関数をモナド空間に「持ち上げる」のと同じようなもので、コアの回答タイプにエラーが表示され、例外のスローとキャッチが効果的に行われます。
多分モナドは基本的にほとんどの主流言語の「nullはエラーをチェックする」チェックと同じです(ただし、nullをチェックする必要がある点を除きます)。主に同じ利点と欠点があります。