Scalaz ValidationNel is not working

与世无争的帅哥 提交于 2019-12-08 11:04:34

问题


I tried to use Scalaz(7.2.18) to validate data. I tried following code:

    def hasDob: ValidationNel[AdtError, String] =
      enc.dob.map(_.success).getOrElse(MissingAdmitDate(enc).failureNel)

    def hasAdmitDt: ValidationNel[AdtError, Timestamp] =
      enc.admitDT.map(_.success).getOrElse( MissingAdmitDate(enc).failureNel )

    def hasTimezone: Validation[AdtError, DateTimeZone] =
      fac.timezone.map(_.success).getOrElse( UndefinedTimezone(fac).failureNel )

    (hasDob |@| hasAdmitDt |@| hasTimezone) { (dob, admitTime, facilityTz) => ... }

    case class MissingDateOfBirth(enc: EncAdt)
      extends AdtError { def logString = s"Txn ID: ${enc.transactionID}  Missing DOB: ${enc.accountNumber}" }

    case class MissingAdmitDate(enc: EncAdt)
      extends AdtError { def logString = s"Txn ID: ${enc.transactionID}  Missing Admit Date: ${enc.accountNumber}" }

UPDATE START

//enc
case class EncAdt(transactionID: Long,
     dob: Option[String],
     admitDT: Option[Timestamp],
     ...
)

//fac
case class Hl7Facility(
  timezone: Option[DateTimeZone],
     ...
)

UPDATE END

But above code is throwing compile-time error:

Type mismatch: Validation[Nothing, String], actual: scalaz.ValidationNel[MissingDateOfBirth, Nothing]
Type mismatch: Validation[Nothing, String], actual: scalaz.ValidationNel[MissingAdmitDate, Nothing]
Type mismatch: Validation[Nothing, String], actual: scalaz.ValidationNel[UndefinedTimezone, Nothing]

What am I doing wrong here? Any solution for above issue is highly appreciable.


回答1:


Unfortunately NonEmptyList[A] is invariant so you can't upcast NonEmptyList[MissingAdmitDate] to NonEmptyList[AdtError]. So the code that compiles directly should probably look like this:

def hasAdmitDt: ValidationNel[AdtError, Timestamp] = {
  enc.admitDT.map(_.successNel[AdtError])
    .getOrElse(MissingAdmitDate(enc).asInstanceOf[AdtError].failureNel)
}

This doesn't look nice but I don't see a way to make Scala compiler infer generics the way you need it in this form. However you can create your own custom methods instead of successNel and failureNel.

I mean something like this:

object AdtValidation {

  type AdtValidation[A] = ValidationNel[AdtError, A]

  implicit class AdtValidationSuccess[A](val value: A) extends AnyVal {
    def successAdt: AdtValidation[A] = Validation.success[NonEmptyList[AdtError], A](value)
  }

  implicit class AdtValidationFailure(val value: AdtError) extends AnyVal {
    def failureAdt[A]: AdtValidation[A] = Validation.failureNel[AdtError, A](value)
  }
}

and then you can do something like this:

import AdtValidation._
def hasAdmitDt: AdtValidation[Timestamp] = {
  enc.admitDT.map(_.successAdt).getOrElse(MissingAdmitDate(enc).failureAdt)
}

But then, if you go this way anyway, you may also add something like this

object AdtValidation {

  type AdtValidation[A] = ValidationNel[AdtError, A]

  implicit class AdtValidationOptionOps[A](val value: Option[A]) extends AnyVal {
    def validateNonEmpty(emptyError: => AdtError): AdtValidation[A] = value.map(_.success[NonEmptyList[AdtError]]).getOrElse(Validation.failureNel[AdtError, A](emptyError))
  }  
}

so that you can do

def hasAdmitDt: AdtValidation[Timestamp] = {
  enc.admitDT.validateNonEmpty(MissingAdmitDate(enc))
}

P.S. it looks suspicious that in your code sample inside hasDob you fail with MissingAdmitDate rather than something like MissingDob



来源:https://stackoverflow.com/questions/48058114/scalaz-validationnel-is-not-working

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!