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