4

検証オブジェクトがあります

val v = Validation[String, Option[Int]]

たとえば、実際の整数値が 100 に等しいかどうかを確認するために、2 番目の検証を行う必要があります。私が行った場合

val vv = v.map(_.map(intValue => if (intValue == 100) 
                               intValue.success[String] 
                           else 
                               "Bad value found".fail[Integer]))

私は得る:

Validation[String, Option[Validation[String, Int]]]

vv を Validation[String, Option[Int]] としても最も簡潔な方法で取得するにはどうすればよいですか

=========

私自身から可能な解決策を見つけました:

val validation: Validation[String, Option[Int]] = Some(100).success[String]

val validatedTwice: Validation[String, Option[Int]] = validation.fold(
  _ => validation,                             // if Failure then return it
  _.map(validateValue _) getOrElse validation  // validate Successful result
)

def validateValue(value: Int): Validation[String, Option[Int]] = {
  if (value == 100)
    Some(value).success[String]
  else
    "Bad value".fail[Option[Int]]
}

機能しますが、簡潔でエレガントに見えません

==============

私自身の2番目の解決策ですが、複雑すぎるようにも見えます:

val validatedTwice2: Validation[String, Option[Int]] = validation.flatMap(
    _.map(validateValue _).map(_.map(Some(_))) getOrElse validation)

def validateValue(value: Int): Validation[String, Int] = {
    if (value == 100)
      value.success[String]
    else
      "Bad value".fail[Int]
}
4

3 に答える 3

2

あなたのソリューションは複雑すぎます。以下で十分です!

v flatMap (_.filter(_ == 100).toSuccess("Bad value found"))

は、オプションが空の場合に失敗した場合に提供される値を取得して、toSuccessOptionWに変換します。次のような作品:Option[A]Validation[X, A]flatMap

Validation[X, A] 
          => (A => Validation[X, B]) 
                                => (via flatMap) Validation[X, B]

つまり、flatMapマップしてからフラットjoin化します (scalaz 用語で):

Validation[X, A]
          => (A => Validation[X, B]]
                            => (via map) Validation[X, Validation[X, B]]
                                                  =>  (via join) Validation[X, B]
于 2012-02-06T19:52:29.540 に答える
2

最初に、いくつかの型エイリアスを設定しましょう。これを繰り返し入力するとすぐに古くなってしまうからです。ここにいる間に、検証ロジックも少し整理します。

type V[X] = Validation[String, X]
type O[X] = Option[X]
def checkInt(i: Int): V[Int] = Validation.fromEither(i != 100 either "Bad value found" or i)

val v: V[O[Int]] = _

これが私たちの出発点です - b1 はあなたの vv 状況に相当します

val b1: V[O[V[Int]]] = v.map(_.map(checkInt))

V[O[V[Int]]] を V[V[O[Int]]] にひっくり返すオプションを順番に並べてみましょう

val b2: V[V[O[Int]]] = v.map(_.map(checkInt)).map(_.sequence[V, Int])

または、ラムダ式を感じている場合は、そうであった可能性があります

sequence[({type l[x] = Validation[String, x]})#l, Int]

次に、ネストされた検証を平坦化します。実際にはここで fastfail の動作が必要なので、Validation モナドを取り込みますが、これは一般的に正しいことではありません。

implicit val monad = Validation.validationMonad[String]
val b3: V[O[Int]] = v.map(_.map(checkInt)).map(_.sequence[V, Int]).join

これで、Validation[String, Option[Int]] ができたので、ここまで来ましたが、まだかなり面倒です。等式の推論を使用して整理してみましょう

第 2 関手の法則により、次のことがわかります。

X.map(_.f).map(_.g) = X.map(_.f.g) =>
    val i1: V[O[Int]] = v.map(_.map(checkInt).sequence[V, Int]).join

そしてモナドの定義により:

X.map(f).join = X.flatMap(f) =>
    val i2: V[O[Int]] = v.flatMap(_.map(checkInt).sequence[V, Int])

そしてトラバーサルの自由定理を適用します
:

X.map(f).sequence = X.traverse(f andThen identity) = X.traverse(f) =>
    val i3: V[O[Int]] = v.flatMap(_.traverse[V, Int](checkInt))

だから今、私たちはもう少し文明化されたものを見ています。flatMap と traverse にはいくつかの策略があると思いますが、インスピレーションが尽きてしまいました。

于 2012-08-15T11:07:46.353 に答える
0

flatMap次のように使用します。

v.flatMap(_.parseInt.fail.map(_.getMessage).validation)
于 2012-02-06T19:21:40.293 に答える