web-dev-qa-db-ja.com

Scalaで例外を投げる、「公式ルール」とは

私はScala Courseraのコースをフォローしています。私はScala Oderskyの本も読み始めました。

私がよく耳にすることは、関数型言語で例外をスローすることは良い考えではないということです。なぜなら、それは制御フローを壊し、通常はFailureまたはSuccessのいずれかを返すからです。また、Scala 2.10はその方向に進むTryを提供するようです。

しかし、本とコースでは、Martin Oderskyは(少なくとも今のところ)例外は悪いとは言わないようで、彼はそれらを頻繁に使用しています。また、メソッドがアサート/要求することに気づきました...

最後に、ベストプラクティスに従うことを望んでいるので、少し混乱していますが、それらは明確ではなく、言語は両方向に進むようです...

誰かが私がどのような場合に使用すべきかを説明できますか?

49

基本的なガイドラインは、本当に例外的なものには例外を使用することです**。 「通常の」障害の場合は、OptionまたはEitherを使用することをお勧めします。誰かが間違った方法でくしゃみをしたときに例外がスローされるJava)とインターフェースしている場合は、Tryを使用して安全を保つことができます。

いくつか例を見てみましょう。

マップから何かを取得するメソッドがあるとします。何がおかしいのでしょうか?さて、ドラマチックで危険なもの セグフォルト*スタックオーバーフロー、または要素のような何かが見つからない。あなたにさせます セグフォルト スタックオーバーフローは例外をスローしますが、要素が見つからない場合は、Option[V]値または例外(またはnull)の代わりに?

ここで、ユーザーがファイル名を入力することになっているプログラムを書いているとします。さて、何かがうまく行かなくなったときにプログラムを即座に保釈するのではない場合、Eitherが道です。

def main(args: Array[String]) {
  val f = {
    if (args.length < 1) Left("No filename given")
    else {
      val file = new File(args(0))
      if (!file.exists) Left("File does not exist: "+args(0))
      else Right(file)
    }
  }
  // ...
}

ここで、スペースで区切られた数字を使用して文字列を解析するとします。

val numbers = "1 2 3 fish 5 6"      // Uh-oh
// numbers.split(" ").map(_.toInt)  <- will throw exception!
val tried = numbers.split(" ").map(s => Try(s.toInt))  // Caught it!
val good = tried.collect{ case Success(n) => n }

したがって、さまざまなタイプの障害に対処するために、少なくとも3つの方法があります。機能しないことが予期される動作であり、衝撃的で警告的な障害ではない場合、Optionは機能しました。 Eitherは、物事が機能するかしないか(または、実際には、相互に排他的な2つのオプションがある場合)で、何がうまくいかなかったかについての情報を保存したい場合に使用します。そして、Tryは、自分で例外を処理するという頭痛の種になりたくないが、それでも例外に満足するコードとのインターフェースが必要な場合です。

ちなみに、例外は良い例になります。したがって、他の場所よりも教科書や学習資料で頻繁に見つけるでしょう、私は思う:教科書の例は非常に不完全であることが多いので、通常、慎重な設計によって防止される深刻な問題が必要です代わりに、例外をスローしてフラグを立てます。

*編集:セグメンテーション違反はJVMをクラッシュさせるため、バイトコードに関係なく発生することはありません。その場合でも例外は役に立ちません。スタックオーバーフローを意味しました。

**編集:例外(スタックトレースなし)は、Scalaの制御フローにも使用されます。実際には非常に効率的なメカニズムであり、ライブラリ定義のbreakステートメントや、コントロールからメソッドから返されるreturnなどを有効にします実際に1つ以上のクロージャーに渡されました。ほとんどの場合、これらの制御フロー例外の1つを誤ってキャッチする可能性があるため、allThrowablesをキャッチすることはそれほど素晴らしい考えではないことを理解することを除いて、これについて自分で心配する必要はありません。

47
Rex Kerr

したがって、これは、Scalaは、特にJavaのようなレガシー言語および環境との移行の容易性/相互運用性のために、機能的な純度をトレードオフする場所の1つです。参照整合性を破り、等式で推論することを不可能にするため(もちろん、非終了再帰は同じことを行いますが、それらを不可能にする制限を強制する言語はほとんどありません。)機能的な純度を保つには、Option /多分/いずれか/試行/検証、すべて成功または失敗を参照透過型としてエンコードし、それらが提供するさまざまな高階関数または基礎となる言語の特別なモナド構文を使用して、物事を明確にします。これは、Scalaで「null」を使用する、または可変コレクション、またはローカル「var」を使用するのに似ています。 、そして、それの大部分にはしないでください、しかし、誰もが期限内です。

11
Dave Griffith