Кошки Скала, Проверенные с Параметризованным Результатом Проверки - PullRequest
0 голосов
/ 16 октября 2018

Я следую примеру в https://typelevel.org/cats/datatypes/validated.html, но с изюминкой: тип элементов в NEL - не DomainValidation, а DomainValidation [A], и я не уверен, как это сделать.Ниже моя попытка, но у меня проблемы с типами.Вероятно, это такой же вопрос Scala, как и вопрос, подтвержденный Cats.

import cats.data._
import cats.implicits._

sealed trait Widget
case object WidgetA extends Widget
case object WidgetB extends Widget

object Widget{
  val values = List(WidgetA, WidgetB)
  def fromString(s: String): Option[Widget] = values.filter(_.toString == s).headOption
}

case class Order( quantity: Float, widget: Widget )

trait MyValidation[A] {
  def errorMessage: String
  def is: A
}

type ValidationResult[A,B] = ValidatedNel[MyValidation[A], B]

case class WidgetQuantityIsNegative(is: Float) extends MyValidation[Float] {
  def errorMessage = s"$is widget quantity is negative"
}

case class WidgetTypeIsInvalid(is: String) extends MyValidation[String] {
  def errorMessage = s"$is is not a valid widget type"
}

def validateQuantity(q: Float): ValidationResult[Float, Float] =
  if(q >= 0) q.validNel else WidgetQuantityIsNegative(q).invalidNel

/*
Here I would like to validate the string.
If it represents a valid Widget, return the valid Widget
else return the invalid */
def validateWidgetType(s: String) =
  Widget.fromString(s) map {widget => widget.validNel } getOrElse WidgetTypeIsInvalid.invalidNel

def validateOrder( quantity: Float, s: String ): ValidationResult[MyValidation[Any], Order] = // NOT SURE IF ANY IS THE RIGHT TYPE
  (
    (validateQuantity(quantity)),
    (validateWidgetType(s))
  ).mapN[Order](Order.apply) // THIS DOES NOT COMPILE

1 Ответ

0 голосов
/ 16 октября 2018

Я немного подправил ваш код, и он скомпилировал:

import cats.data._
import cats.implicits._

sealed trait Widget
case object WidgetA extends Widget
case object WidgetB extends Widget

object Global {

  object Widget {
    val values = List(WidgetA, WidgetB)

    def fromString(s: String): Option[Widget] = values.find(_.toString == s)
  }

  case class Order(quantity: Float, widget: Widget)

  trait MyValidation[+A] {
    def errorMessage: String

    def is: A
  }

  type ValidationResult[A, B] = ValidatedNel[MyValidation[A], B]

  case class WidgetQuantityIsNegative(is: Float) extends MyValidation[Float] {
    def errorMessage = s"$is widget quantity is negative"
  }

  case class WidgetTypeIsInvalid(is: String) extends MyValidation[String] {
    def errorMessage = s"$is is not a valid widget type"
  }

  def validateQuantity(q: Float): ValidationResult[Float, Float] =
    if (q >= 0) q.validNel else WidgetQuantityIsNegative(q).invalidNel

  /*
Here I would like to validate the string.
If it represents a valid Widget, return the valid Widget
else return the invalid string */
  def validateWidgetType(s: String): ValidationResult[String, Widget] =
    Widget.fromString(s) map { widget => widget.validNel } getOrElse WidgetTypeIsInvalid(s).invalidNel

  def validateOrder(quantity: Float, s: String): ValidationResult[Any, Order] = 
    (
      validateQuantity(quantity),
      validateWidgetType(s)
    ).mapN[Order](Order(_, _)) // Order.apply without _ works too but would add one more red line in intellij

} 

По сути, все, что я сделал, было пометить MyValidation ковариантно и зафиксировать validateWidgetType и validateQuantity для возврата того же типа


ПРИМЕЧАНИЕ о ковариации:

когда мы удаляем аннотацию ковариации, появляется следующее сообщение компилятора:

Error:(50, 18) type mismatch;
 found   : cats.data.Validated[cats.data.NonEmptyList[cats.Global.MyValidation[_ >: String with Float]],cats.Global.Order]
 required: cats.Global.ValidationResult[Any,cats.Global.Order]
    (which expands to)  cats.data.Validated[cats.data.NonEmptyList[cats.Global.MyValidation[Any]],cats.Global.Order]
Note: cats.data.NonEmptyList[cats.Global.MyValidation[_ >: String with Float]] <: Any, but class Validated is invariant in type A.
You may wish to define A as +A instead. (SLS 4.5)
    ).mapN[Order](Order.apply)

, которое более или менее говорит, что конкретный тип, которыйявляется подтипом Float и String не совсем Any, а подтипом Any

...