2013-05-01 9 views
10

さまざまな種類のエラーをエレガントに処理するために、 。[結果]、IO [結果]、最後に使用する必要がある[エラー、結果]のいずれかを試してください

この質問は、どういうわけかScalaでのエラー処理について私がすでに持っていた多くの質問の要約です。あなたはここにいくつかの質問を見つけることができます。今のところ


を、私は次のことを理解して:

  • どちらかのように使用することができます結果
  • てみてください失敗する可能性があり、メソッド呼び出しのためのラッパーは、右の障害が致命的でない例外
  • されているいずれかをbiaisedれるIO(scalaz)全3を簡単にしているIO操作
  • を扱う純粋な方法を構築することができますすべての3が、彼らは我々が本当に例外の例外をスローする必要があります
  • 致命的でない限り、私たちは通常、例外をスローしない官能langagesで
  • 互換性がないためflatMap方法の理解のためで簡単に混合可能ではありません理解
  • のために用いることのできます条件。私は、これはスロー可能オブジェクトは、JVMのパフォーマンスコストが作成
  • を試してみてのアプローチであり、

    今業務フローcontrole


リポジトリ層に使用されるものではないと思います私はUserRepositoryを持っていると考えてください。 UserRepositoryはユーザーを格納し、findByIdメソッドを定義します。以下の障害が発生する可能性があります:

  • を致命的な失敗(OutOfMemoryError
  • アンIO障害データベースが読み取り/アクセスできないため

また、ユーザーはOption[User]につながる、不足していることができ結果

リポジトリのJDBC実装を使用すると、SQLを使用して、致命的ではない例外(制約違反など)をスローすることができます。

私たちがIO操作を扱っているので、IOモナドは、純粋な機能が必要な場合にも意味があります。

だから、結果の型は次のようになります。それ以外の

  • Try[Option[User]]
  • IO[Option[User]]
  • 何か?

サービス層

今度は、リポジトリの以前に定義されたfindByIdを使用して、いくつかの方法updateUserName(id,newUserName)を提供するビジネス層、UserServiceを、ご紹介しましょう。

以下の障害が起こる可能性:サービス層に伝播

  • すべてのリポジトリの障害を
  • ビジネス・エラー:
  • ビジネス・エラーが存在していないユーザーのユーザー名を更新することはできません。新しいユーザー名が短すぎる

そして、結果の型は次のようになります。

  • Try[Either[BusinessError,User]]
  • IO[Either[BusinessError,User]]
  • 他の何か?

例外的なエラーではないため、BusinessErrorはThrowableではありません。


私はメソッド呼び出しを組み合わせることのために、内包表記を使用して維持したいため、内包

使用。

異なるモナドを簡単に混在させることはできませんので、すべての操作に一貫したリターンタイプを用意する必要があります。

現実世界のScalaアプリケーションでは、さまざまな種類のエラーが発生した場合にも補完機能を使い続けることができます。

今のところ、すべてがEither[Error,Result]を返すサービスとリポジトリを使用して、分かり易いものが私のためにうまく動作しますが、すべての種類の失敗が一緒に溶けてしまい、これらの失敗を処理するためのハックになります。

for-comprehensionsを使用できるように、異なる種類のモナド間で暗黙的な変換を定義していますか?

エラーを処理するために独自のモナドを定義していますか?

ところで、私はすぐに非同期IOドライバを使用しています。 だから私は私の戻り値の型はさらに複雑になることができると思います:IO[Future[Either[BusinessError,User]]]


私のアプリケーションは空想ではないながら、私は本当に、使用するかわからないので、何かアドバイスは歓迎されるでしょう:それはどこ単なるAPIであります私は、クライアント側に表示できるビジネスエラーと技術的なエラーを区別することができます。私はエレガントで純粋な解決策を見つけようとします。

+0

あなたはこれを書いたところでいくつかのコードを投稿してください。私は同じようなパターンを念頭に置いているので、とても興味があります。私のスカラズのスキルは、私が望むものではありません...私はこれらのクラスを理解するのに苦労しています。 – costa

答えて

11

これはScalazのEitherTモナドトランスのためのものです。 IO[Either[E, A]]のスタックは、EitherT[IO, E, A]に相当します。ただし、前者は複数のモナドとして順に処理する必要がありますが、後者は自動的に基本モナドIOEitherの機能を追加する単一のモナドです。 EitherT[Future, E, A]を同様に使用して、例外ではないエラー処理を非同期操作に追加できます。

一般に、モナドトランスは、複数のモナドを単一のオペレーションで混在させる必要性に対する答えです。


EDIT:

私はあなたがScalazバージョン7.0.0を使用していると仮定します。 IOモナドの上にEitherTモナド変換子を使用するためには

、あなたが最初にScalazの関連部分をインポートする必要があります。

import scalaz._, scalaz.effect._ 
あなたはまた、あなたのエラータイプを定義する必要があり

RepositoryErrorBusinessErrorを、これはいつものように機能します。 RepositoryErrorBusinessErrorに変換してパターンマッチして、正確なエラータイプを回復できるかどうかを確認するだけです。あなたのメソッドのシグネチャがなっ

その後:

def findById(id: ID): EitherT[IO, RepositoryError, User] 
def updateUserName(id: ID, newUserName: String): EitherT[IO, BusinessError, User] 

あなたの各方法の中で、あなたはfor -comprehensionsで利用可能な単一の統合モナドとしてEitherT - と - IOベースのモナドスタックを使用することができますいつものように。 EitherTは、計算全体を通じてベースモナド(この場合はIO)をスレッド処理し、またEitherのようなエラーも処理します(既定では右寄せされていますので、常にすべてを扱う必要はありません)。通常.rightジャンク)。 IO操作を実行したい場合は、インスタンスメソッドIOを使用して、結合されたモナドスタックにそれを持ち上げるだけです。

このようにして作業するとき、コンパニオンオブジェクトの機能は非常に便利です。

+1

ありがとう、これはまさに私が必要としているようです。不幸にも私はScalazのスキルが本当にどういうものなのかを理解することはできません。(上記のメソッドシグニチャを使用して、簡単な使用方法を教えてください) –

+0

@SebastienLorber私は自分の答えを編集しようとしました。 –

+1

@SebastienLorber 1つのScalazのヒント。どのように動作するかをよりよく理解するために、いくつかのものを自分で再実装することをお勧めします。この場合、 'EitherT'のMonadインスタンスを実装することは本当に良い練習です。 – Eric

関連する問題