Scala隐式证据检查时类型擦除问题求解(精简版Refined Types)
问题分析
你的代码中check方法的返回类型被编译器统一为Validator[_ <: Validate](因为if-else分支返回的Validator[Pass]和Validator[Fail]的公共超类型是Validator[Validate]),编译时无法静态确定具体的State类型,因此调用validate时无法自动生成State =:= SUCCESS的隐式证据,导致报错。
而testResult能返回true是因为它用了TypeTag在运行时检查类型,但编译时的类型信息已经被擦除,没法支撑validate的编译期类型检查。
解决技巧
下面提供几种Scala中可行的解决方案:
方案一:用Either分离成功/失败类型
最直观的方式是让check返回Either[Validator[Fail], Validator[Pass]],通过模式匹配在编译期明确分支类型:
sealed trait Validate sealed trait Pass extends Validate sealed trait Fail extends Validate type SUCCESS = Pass case class Validator[State <: Validate: TypeTag](s: String = "") { def check: Either[Validator[Fail], Validator[Pass]] = { if (s == "awesome") Right(Validator[Pass](s)) else Left(Validator[Fail](s)) } def pass: Validator[Pass] = Validator[Pass](s) def fail: Validator[Fail] = Validator[Fail](s) def validate(implicit ev: State =:= SUCCESS): String = s def testResult = typeOf[State] =:= typeOf[SUCCESS] } // 使用示例 Validator("awesome").check match { case Right(valid) => valid.validate // 编译通过,返回"awesome" case Left(invalid) => println("字符串不符合要求") }
方案二:用类型类引导编译期类型推导(Scala 2.13+)
通过定义类型类,把字符串的验证逻辑映射到类型级别,让编译器在调用check时推导正确的State类型:
import scala.reflect.runtime.universe._ sealed trait Validate sealed trait Pass extends Validate sealed trait Fail extends Validate type SUCCESS = Pass // 类型类:关联字符串字面量与验证状态类型 trait ValidationResult[S <: String] { type Out <: Validate } object ValidationResult { type Aux[S <: String, O <: Validate] = ValidationResult[S] { type Out = O } // 匹配"awesome"字符串,对应Pass类型 implicit def passResult[S <: String](implicit ev: S =:= "awesome"): Aux[S, Pass] = new ValidationResult[S] { type Out = Pass } // 默认匹配所有其他字符串,对应Fail类型 implicit def failResult[S <: String]: Aux[S, Fail] = new ValidationResult[S] { type Out = Fail } } case class Validator[State <: Validate: TypeTag](s: String = "") { // 利用类型类推导返回类型 def check(implicit vr: ValidationResult[s.type]): Validator[vr.Out] = Validator[vr.Out](s) def validate(implicit ev: State =:= SUCCESS): String = s def testResult = typeOf[State] =:= typeOf[SUCCESS] } // 使用示例(需开启Scala 2.13编译器选项:-Yliteral-types) val valid = Validator("awesome").check valid.validate // 编译通过 val invalid = Validator("bad").check // invalid.validate // 编译报错,符合预期
方案三:Scala 3 专属:Inline + Match Types
Scala 3的inline方法和match types可以更简洁地实现编译期类型推导:
sealed trait Validate sealed trait Pass extends Validate sealed trait Fail extends Validate type SUCCESS = Pass case class Validator[State <: Validate](s: String = "") { inline def check: Validator[CheckResult[s.type]] = Validator(s) def validate(using ev: State =:= SUCCESS): String = s } // 编译期匹配类型:根据字符串字面量映射到验证状态 type CheckResult[S <: String] = S match case "awesome" => Pass case _ => Fail // 使用示例 Validator("awesome").check.validate // 编译通过 // Validator("bad").check.validate // 编译报错,符合预期
内容的提问来源于stack exchange,提问作者Led
相关产品推荐
相关产品推荐

