Объедините допустимые значения Seq[ValidatedNel]

У меня есть следующий сценарий:

case class MyString(str: String)

val val1: ValidatedNel[String, MyString] = MyString("valid1").validNel
val val2: ValidatedNel[String, MyString] = MyString("valid2").validNel
val val3: ValidatedNel[String, MyString] = "invalid".invalidNel
val vals = Seq(val1, val2, val3)
//vals: Seq[cats.data.Validated[cats.data.NonEmptyList[String],MyString]] = List(Valid(MyString(valid)), Invalid(NonEmptyList(invalid)))

В конце я хотел бы иметь возможность сделать match для результата и получить все ошибки или все допустимые значения в виде последовательности.

Мой вопрос: как преобразовать Seq[Validated[NonEmptyList[String],MyString]] в Validated[NonEmptyList[String],Seq[MyString]]]

Итак, мой первый проход состоял в том, чтобы реализовать Semigroup для Seq[MyString]:

implicit val myStringsAdditionSemigroup: Semigroup[Seq[MyString]] = new Semigroup[Seq[MyString]] {
  def combine(x: Seq[MyString], y: Seq[MyString]): Seq[MyString] = x ++ y
}

... который работает:

Seq(val1, val2).map(_.map(Seq(_))).reduce(_ |+| _)
//res0: cats.data.Validated[cats.data.NonEmptyList[String],Seq[MyString]] = Valid(List(MyString(valid1), MyString(valid2)))

но мне нужно подготовить свои данные, заключив все допустимые значения в Seq... что кажется странным. Итак, может быть, есть лучший способ сделать это?


person Todor Kolev    schedule 24.10.2017    source источник


Ответы (1)


Если вы используете что-то кроме Seq, например Vector или List, вы можете sequence использовать его.

sequence в основном выворачивает конструктор типов наизнанку. Это означает превращение F[G[A]] в G[F[A]]. Чтобы это работало, F должно быть Traverse, а G должно быть Applicative. К счастью, Validated — это Applicative, а List или Vector — экземпляры Traverse.

Итак, в итоге ваш код должен выглядеть примерно так:

import cats.implicits._

val validatedList: Validated[NonEmptyList[String],List[MyString]]] = 
  vals.sequence

Примечание: если это не скомпилируется для вас, вам может потребоваться включить partial-unification.

Самый простой способ включить partial-unification — добавить sbt-partial-unification плагин.

Если вы используете Scala 2.11.9 или новее, вы также можете просто добавить флаг компилятора:

scalacOptions += "-Ypartial-unification"

Мы из команды cats настоятельно рекомендуем вам всегда держать этот флажок при использовании кошек, так как это значительно упрощает задачу.

person Luka Jacobowitz    schedule 25.10.2017
comment
Проект, в котором я пытался использовать это, - это проект maven. Означает ли это, что нет возможности включить частичную унификацию и, следовательно, использовать эту функциональность? - person Todor Kolev; 16.01.2018
comment
Нет, вы можете использовать их нормально, просто поместите аргументы компилятора внутрь <args>, которые должны быть внутри <configuration> :) - person Luka Jacobowitz; 16.01.2018