Много лет назад я написал длинный блог о основной проблеме, с которой вы здесь сталкиваетесь, если вам интересна история или какие-то старые обходные пути, но, к счастью, теперь решение стало намного проще(при условии, что вы используете Scala 2.11 или 2.12): просто добавьте -Ypartial-unification
в опции компилятора Scala.Например, если вы используете sbt, это может выглядеть так:
scalacOptions += "-Ypartial-unification"
И все готово.
Если по какой-то причине вы не можете добавить опцию компилятора,Вы должны будете предоставить некоторые параметры типа явно.Вот краткая упрощенная версия:
import cats.data.Validated, cats.implicits._
case class Foo(i: Int) {
type ValidationResult[A] = Validated[List[String], A]
def check: ValidationResult[Boolean] =
if (i < 0) Validated.invalid(List("bad")) else Validated.valid(true)
}
case class Foos(values: List[Foo]) {
type ValidationResult[A] = Validated[List[String], A]
def dup: ValidationResult[Unit] = Validated.valid(())
def check: ValidationResult[Boolean] =
(dup, values.map(_.check).sequence).mapN((_, _) => true)
}
Это приведет к ошибке с ошибкой, которую вы увидели (при условии, что вы не добавили -Ypartial-unification
):
<console>:22: error: Cannot prove that cats.data.Validated[List[String],Boolean] <:< G[A].
(dup, values.map(_.check).sequence).mapN((_, _) => true)
^
Чтобы исправить это, вы можете написатьследующее:
case class Foos(values: List[Foo]) {
type ValidationResult[A] = Validated[List[String], A]
def dup: ValidationResult[Unit] = Validated.valid(())
def check: ValidationResult[Boolean] =
(dup, values.map(_.check).sequence[ValidationResult, Boolean]).mapN((_, _) => true)
}
Я думаю, вы также можете просто переместить псевдоним типа на уровень пакета, но я не уверен на 100% в этом и не заинтересован в проверке, извините.
Одна сноска: в любое время, когда у вас есть map
, а затем sequence
, вы можете немного ускорить процесс, используя вместо этого traverse
:
def check: ValidationResult[Boolean] =
(dup, values.traverse[ValidationResult, Boolean](_.check)).mapN((_, _) => true)
Снова вы можете сбросить параметры типа ипозвольте выводу типа выяснить их, если у вас включен -Ypartial-unification
.