You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Scalaz 7.2.18中ValidationNel使用报错,求解决方案

问题:Scalaz 7.2.18 数据验证编译错误

背景

我尝试用Scalaz 7.2.18做数据验证,写了下面的代码,但编译时抛出了类型不匹配的错误,想请教下问题出在哪以及怎么解决。

我的代码实现

验证逻辑

def hasDob: ValidationNel[AdtError, String] = enc.dob.map(_.success).getOrElse(MissingDateOfBirth(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}" }

// 补充的数据模型
case class EncAdt(transactionID: Long, dob: Option[String], admitDT: Option[Timestamp], ... )
case class Hl7Facility( timezone: Option[DateTimeZone], ... )

编译错误信息

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]


问题分析

这几个错误本质是类型不一致导致的,主要有两个关键点:

  1. Validation和ValidationNel不兼容:你写的hasTimezone返回的是普通Validation,但另外两个函数返回的是ValidationNel(带NonEmptyList的验证类型)。Scalaz的|@|(Applicative Builder)要求所有参与组合的验证必须是同一种类型,错误通道得统一是NonEmptyList[E]才行。
  2. 错误类型的自动推导失效:在getOrElse里,你直接返回了具体错误子类的failureNel,Scalaz有时候没法自动把这些子类推导成父类AdtError,导致类型不匹配。

解决方案

1. 统一所有验证函数的返回类型为ValidationNel

先把hasTimezone改成返回ValidationNel[AdtError, DateTimeZone],注意要用successNel代替success,这样成功通道也符合Nel的类型:

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

2. 显式指定错误类型,帮助Scalaz推导

在failureNel后面显式指定父类AdtError,避免Scalaz把错误类型锁定在具体的子类上:

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

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

这里用successNel是因为它返回的是ValidationNel[Nothing, T],和getOrElse里的ValidationNel[AdtError, Nothing]组合后,Scalaz会自动合并成我们需要的ValidationNel[AdtError, T]。

3. 修正后的完整代码

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

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

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

// 现在|@|可以正常组合三个验证了
(hasDob |@| hasAdmitDt |@| hasTimezone) { (dob, admitTime, facilityTz) => 
  // 这里写你的业务处理逻辑,比如构造验证后的对象
}

内容的提问来源于stack exchange,提问作者Sujit Baniya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:56:18