scalazを使用すると、のValidation[E, A]
ようEither[E, A]
になりますが、ifE
が半群(リストのように連結できるものを意味します)である場合、複数の検証済み結果を組み合わせて、発生したすべてのエラーを保持できるという特性があります。
たとえば、Scala2.10-M6とScalaz7.0.0-M2を使用します。ここで、Scalazには、デフォルトで右バイアスされたEither[L, R]
名前のカスタムがあります。\/[L, R]
import scalaz._, Scalaz._
implicit class EitherPimp[E, A](val e: E \/ A) extends AnyVal {
def vnel: ValidationNEL[E, A] = e.validation.toValidationNEL
}
def parseInt(userInput: String): Throwable \/ Int = ???
def fetchTemperature: Throwable \/ Int = ???
def fetchTweets(count: Int): Throwable \/ List[String] = ???
val res = (fetchTemperature.vnel |@| fetchTweets(5).vnel) { case (temp, tweets) =>
s"In $temp degrees people tweet ${tweets.size}"
}
これresult
は、Validation[NonEmptyList[Throwable], String]
発生したすべてのエラー(温度センサーエラーおよび/またはTwitterエラーまたはなし)または成功したメッセージのいずれかを含むです。\/
その後、便宜上に戻ることができます。
注:EitherとValidationの違いは、主に、Validationを使用するとエラーを蓄積できますがflatMap
、蓄積されたエラーを失うことはできませんが、Eitherを使用すると(簡単に)蓄積することはできませんがflatMap
(または理解のために)失う可能性があります最初のエラーメッセージを除くすべて。
エラー階層について
これはあなたにとって興味深いかもしれないと思います。scalaz / Either
/ \/
/を使用するかどうかに関係なく、Validation
開始は簡単でしたが、今後は追加の作業が必要になることを経験しました。問題は、複数の誤った関数から意味のある方法でエラーをどのように収集するかということです。確かに、どこでも、Throwable
またはList[String]
どこでも使用でき、簡単な時間を過ごすことができますが、あまり使いやすく、解釈しやすいようには聞こえません。「子供の年齢がありません」::「IOエラー読み取りファイル」::「ゼロ除算」などのエラーのリストを取得することを想像してみてください。
したがって、私の選択は、Javaのチェックされた例外を階層にラップするのと同じように、(ADTを使用して)エラー階層を作成することです。例えば:
object errors {
object gamestart {
sealed trait Error
case class ResourceError(e: errors.resource.Error) extends Error
case class WordSourceError(e: errors.wordsource.Error) extends Error
}
object resource {
case class Error(e: GdxRuntimeException)
}
object wordsource {
case class Error(e: /*Ugly*/ Any)
}
}
次に、エラータイプが異なる関数のエラーの結果を使用する場合、関連する親エラータイプの下でそれらを結合します。
for {
wordSource <-
errors.gamestart.WordSourceError <-:
errors.wordsource.Error <-:
wordSourceCreator.doCreateWordSource(mtRandom).catchLeft.unsafePerformIO.toEither
resources <-
errors.gamestart.ResourceError <-:
GameViewResources(layout)
} yield ...
ここでは、Bifunctorであるため、左側のf <-: e
関数をマップします。あなたのために持っているかもしれません。f
e: \/
\/
se: scala.Either
se.left.map(f)
これは、シェイプレス HListIso
を提供して適切なエラーツリーを描画できるようにすることでさらに改善される可能性があります。
改訂
更新:(e: \/).vnel
障害側をに引き上げ、障害が発生したNonEmptyList
場合、少なくとも1つのエラーが発生します(以前は:またはなし)。