代码之家  ›  专栏  ›  技术社区  ›  dev

Scala Cats已验证:value mapN不是ValidatedNel元组的成员

  •  14
  • dev  · 技术社区  · 6 年前

    Scala社区。

    目前,我正在尝试使用cats实现自定义模型/单参数验证 Validated 单子。但是,从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)
      }
    }
    

    编译器说

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

    请告诉我如何使用新的应用程序语法或我做错了什么。。。(忘记创建/导入一些隐式)

    2 回复  |  直到 6 年前
        1
  •  11
  •   Andrey Tyukin    6 年前

    您似乎缺少以下导入:

    import cats.syntax.apply._
    

    对于 mapN

    请确保您 -Ypartial-unification 编译器标志已激活,否则编译器将很难提取 ValidatedNel[FieldErrorInfo, ?] 从类型 stage1 stage2 :

    libraryDependencies += "org.typelevel" %% "cats-core" % "1.1.0"
    scalaVersion := "2.12.5"
    scalacOptions += "-Ypartial-unification"
    

    通过上述设置,以下功能可以正常工作:

    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)
      }
    }
    
        2
  •  7
  •   Andrii Stefaniv    6 年前

    价值观 stage1 stage2 必须具有类型 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)
      }
    }