Проверка Скалаза, проверка внутреннего значения - PullRequest
4 голосов
/ 06 февраля 2012

У меня есть объект проверки

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

Мне нужно сделать вторую проверку, чтобы проверить, например, равно ли фактическое целочисленное значение 100. Если я сделаю

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 также в виде валидации [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]]
}

выглядит не лаконично и элегантно, хотя работает

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

Второе решение от моего, но выглядит слишком сложным:

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]
}

Ответы [ 3 ]

2 голосов
/ 06 февраля 2012

Ваше решение слишком сложное. Следующего будет достаточно!

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

toSuccess происходит от OptionW и преобразует Option[A] в Validation[X, A], принимая значение, предоставленное для случая сбоя, если опция пуста. flatMap работает так:

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

То есть flatMap отображает, а затем выравнивает (join на языке скалярного выражения):

Validation[X, A]
          => (A => Validation[X, B]]
                            => (via map) Validation[X, Validation[X, B]]
                                                  =>  (via join) Validation[X, B]
2 голосов
/ 15 августа 2012

Во-первых, давайте настроим псевдонимы некоторых типов, потому что их многократное повторение довольно быстро устареет. Пока мы здесь, мы немного исправим вашу логику проверки.

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]

Затем мы сгладим эту вложенную проверку - мы собираемся включить монаду Validation, потому что мы действительно хотим, чтобы здесь выполнялось поведение fastfail, хотя это, как правило, неправильно.

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

Итак, теперь у нас есть проверка [String, Option [Int]], так что мы здесь, но это все еще довольно грязно. Давайте использовать некоторые эквациональные рассуждения, чтобы привести в порядок

По второму закону функторов мы знаем, что:

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 можно поиграть, но у меня кончилось вдохновение.

0 голосов
/ 06 февраля 2012

Используйте flatMap, вот так:

v.flatMap(_.parseInt.fail.map(_.getMessage).validation)
...