Как использовать аппликативные функторы для объединения проверок Scalaz

Не могу понять, можно ли написать что-то подобное, используя Scalaz 7. Я попытался выразить себя с помощью комментариев внутри блока кода.

def validate1(p: String) = ValidationNel[String, Value] = ...
def validate2(p: String) = ValidationNel[String, Value] = ...

validateCombination(p1: String, p2: String) = {
  // I would like to write something like
  (validate1(p1) |@| validate2(p2)) { (v1, v1) =>
    // And validate the combinations here and return successNel of failNel
  }
}

def validate(p1: String, p2: String, p3: String) = {
  (validateCombination(p1, p2) |@| validate1(p3)) { (v1, v2, v3) =>
    // Notice the three parameters I want to have here
  }
}

Я просто получаю разные типы запутанных ошибок компиляции в validateCombinations или всего 2 параметра для аппликативного функтора, который я получаю внутри функции проверки, один из которых имеет тип ValidationNel[...].


person Lauri    schedule 04.06.2013    source источник


Ответы (1)


Вы можете использовать .flatMap(identity) в методе validateCombination для создания ValidationNel[String, (Value, Value)] и pattern matching в методе validate следующим образом:

def validateCombination(p1: String, p2: String): ValidationNel[String, (Value, Value)] = {
  // I would like to write something like
  (validate1(p1) |@| validate2(p2)) { (v1, v1) =>
    (v1, v2).successNel[String]
  }.flatMap(identity)
}

def validate(p1: String, p2: String, p3: String) = {
  (validateCombination(p1, p2) |@| validate1(p3)) { case ((v1, v2), v3) =>
    // Notice the three parameters I want to have here
  }
}

flatMap(идентификация)

Обычно вы должны использовать метод flatten для вложенных контейнеров, чтобы получить M[T] из M[M[T]]. Работает с Future, Option, Try, коллекциями и так далее.

В данном случае type M[T] = ValidationNel[String, T].

Я не знаю, почему в Validation нет метода flatten, но вы всегда можете использовать flatMap(identity) вместо flatten.

соответствие

Как Бен Джеймс отмечено, flatMap на Validation сомнительно. Вы всегда можете использовать match вместо него:

(validate1(p1) |@| validate2(p2)) { (v1, v1) =>
  (v1, v2).successNel[String]
} match {
  case Success(s) => s
  case Failure(f) => Failure(f)
}

соответствие шаблону

Сопоставление с образцом — распространенный способ работы с кортежами. Например, это может быть полезно с методом foldLeft, например foldLeft(1 -> 2){ case ((a, b), c) => ??? }.

Если вы обнаружите, что используете геттеры _N на Tuple, вам, вероятно, следует использовать сопоставление с образцом.

для понимания

Как Дэниел С. Собрал отмечено для лучшего понимания.

Вы можете использовать его в своем методе validate следующим образом:

def validate(p1: String, p2: String, p3: String) = {
  for{
    (v1, v2) <- validateCombination(p1, p2) // pattern matching
    v3 <- validate1(p3)
  } yield ??? // Your code here
}

Он включает сопоставление с образцом без ключевого слова case.

Обратите внимание, что для понимания вызывается flatMap на validateCombination(p1, p2), поэтому вы потеряете сообщения об ошибках от validate1(p3) в случае, если validateCombination(p1, p2) будет Failure. Напротив, |@| собирает все сообщения об ошибках с обеих сторон.

person senia    schedule 04.06.2013
comment
Хотя я бы использовал для понимания - его легче понять, имхо. - person Daniel C. Sobral; 04.06.2013
comment
Validation не имеет flatten, потому что он не предназначен для монады. На самом деле метод flatMap сомнительный. Но есть методы для запуска операций над \/ эквивалентом проверки и обратного преобразования, например. v.disjunctioned(_.flatten) - person Ben James; 04.06.2013
comment
@DanielC.Sobral: вы имеете в виду понимание вместо |@|? Я думал, что смысл в том, чтобы оставить |@| нетронутым. Спасибо за предложение. Вы не возражаете, я добавлю это к моему ответу? - person senia; 04.06.2013
comment
Спасибо за хороший ответ. На самом деле я сначала попробовал это использовать для понимания, но столкнулся с такими проблемами, как не смог найти неявное значение для параметра M: scalaz.Monoid[scalaz.NonEmptyList[String]] при написании кода, например, для (v1 ‹- validate1(p1); v2 ‹ - проверить(p2); если (isValidCombination(v1, v2)) - person Lauri; 04.06.2013
comment
@Lauri: в withFilter "nofollow noreferrer">Validation, поэтому вы не можете использовать оператор if для понимания. См. этот ответ. - person senia; 04.06.2013
comment
Я имел в виду для понимания вместо flatMap. - person Daniel C. Sobral; 04.06.2013