Scala-Cats验证:值mapN不是ValidatedNel元组的成员

dev*_*dev 13 monads functional-programming scala scala-cats scala-2.12

Scala社区.

目前我正在尝试使用猫ValidatedMonad 实现自定义模型/单参数验证.但是,从1.0开始删除笛卡尔积之后,我无法使用(v1 | @ | v2)map(f)并且无法编译我的代码:

import cats.Semigroupal
import cats.data.Validated.{Invalid, Valid}
import cats.data.{ValidatedNel, _}
import cats.implicits._
import cats.instances.all._

case class FieldErrorInfo(name: String, error: String)

type FieldName = String

type ValidationResult[A] = ValidatedNel[FieldErrorInfo, A]

trait SingleFieldValidationRule[U] extends ((U, FieldName) => ValidationResult[U])

trait ModelValidationRule[M] extends (M => ValidationResult[M])

object ValidateNameRule extends SingleFieldValidationRule[String] {
  override def apply(v1: String, name: String): ValidationResult[String]  = {
    if (v1.contains("cats"))
      v1.validNel
    else
      FieldErrorInfo(name, "Some Error").invalidNel
  }
}

object ValidateQuantityRule extends SingleFieldValidationRule[Int] {
  override def apply(v1: Int, name: String): ValidationResult[Int] =
    if (v1 > 0)
      v1.validNel
    else FieldErrorInfo(name, "Some Error").invalidNel
}

case class SampleModel(name: String, quantity: Int)

object ValidateSampleModel extends ModelValidationRule[SampleModel] {
  override def apply(v1: SampleModel): ValidationResult[SampleModel] = {
    val stage1: ValidatedNel[FieldErrorInfo, String] = ValidateNameRule(v1.name, "name")
    val stage2: ValidatedNel[FieldErrorInfo, Int] = ValidateQuantityRule(v1.quantity, "quantity")

    implicit val sga: Semigroupal[NonEmptyList] = new Semigroupal[NonEmptyList] {
      override def product[A, B](fa: NonEmptyList[A], fb: NonEmptyList[B]): NonEmptyList[(A, B)] = fa.flatMap(a => fb.map(b => a -> b))
    }

    (stage1, stage2).mapN(SampleModel)
  }
}
Run Code Online (Sandbox Code Playgroud)

编译说,那

Error:(43, 23) value mapN is not a member of (cats.data.ValidatedNel[FieldErrorInfo,String], cats.data.ValidatedNel[FieldErrorInfo,Int])
    (stage1, stage2).mapN(SampleModel)
                     ^
Run Code Online (Sandbox Code Playgroud)

请指点我如何使用新的Applicative语法或我做错了什么...(忘了创建/导入一些暗示)

And*_*kin 7

您似乎缺少以下导入:

import cats.syntax.apply._
Run Code Online (Sandbox Code Playgroud)

为了mapN.

请确保您已经-Ypartial-unification编译器标志启动,否则编译器将很难提取ValidatedNel[FieldErrorInfo, ?]从类型stage1stage2:

libraryDependencies += "org.typelevel" %% "cats-core" % "1.1.0"
scalaVersion := "2.12.5"
scalacOptions += "-Ypartial-unification"
Run Code Online (Sandbox Code Playgroud)

通过以上设置,以下工作:

import cats.Semigroupal
import cats.data.Validated.{Invalid, Valid}
import cats.data.ValidatedNel
import cats.data.NonEmptyList
import cats.syntax.apply._     // for `mapN`
import cats.syntax.validated._ // for `validNel`

case class FieldErrorInfo(name: String, error: String)

type FieldName = String

type ValidationResult[A] = ValidatedNel[FieldErrorInfo, A]

trait SingleFieldValidationRule[U] extends ((U, FieldName) => ValidationResult[U])

trait ModelValidationRule[M] extends (M => ValidationResult[M])

object ValidateNameRule extends SingleFieldValidationRule[String] {
  override def apply(v1: String, name: String): ValidationResult[String]  = {
    if (v1.contains("cats"))
      v1.validNel
    else
      FieldErrorInfo(name, "Some Error").invalidNel
  }
}

object ValidateQuantityRule extends SingleFieldValidationRule[Int] {
  override def apply(v1: Int, name: String): ValidationResult[Int] =
    if (v1 > 0)
      v1.validNel
    else FieldErrorInfo(name, "Some Error").invalidNel
}

case class SampleModel(name: String, quantity: Int)

object ValidateSampleModel extends ModelValidationRule[SampleModel] {
  override def apply(v1: SampleModel): ValidationResult[SampleModel] = {
    val stage1: ValidatedNel[FieldErrorInfo, String] = ValidateNameRule(v1.name, "name")
    val stage2: ValidatedNel[FieldErrorInfo, Int] = ValidateQuantityRule(v1.quantity, "quantity")

    implicit val sga: Semigroupal[NonEmptyList] = new Semigroupal[NonEmptyList] {
      override def product[A, B](fa: NonEmptyList[A], fb: NonEmptyList[B]): NonEmptyList[(A, B)] = fa.flatMap(a => fb.map(b => a -> b))
    }


    (stage1, stage2).mapN(SampleModel)
  }
}
Run Code Online (Sandbox Code Playgroud)


And*_*niv 6

价值观stage1stage2必须有类型ValidationResult[_].

在这种情况下隐含的mapN应该工作.

 object ValidateSampleModel extends ModelValidationRule[SampleModel] {
  override def apply(v1: SampleModel): ValidationResult[SampleModel] = {
    val stage1: ValidationResult[String] = ValidateNameRule(v1.name, "name")
    val stage2: ValidationResult[Int] = ValidateQuantityRule(v1.quantity, "quantity")

    (stage1, stage2).mapN(SampleModel)
  }
}
Run Code Online (Sandbox Code Playgroud)

  • @monkjack因为`mapN`需要`隐式函子:Functor[F],半群:Semigroupal[F]`,其中`F`(ValidationResult/ValidationNel)是一个具有单个通用参数的容器。`ValidationResult[A]` 有一个参数 `A`。`ValidationNel[A,B]` 有两个类型参数 `A` 和 `B` (3认同)