问题描述
sealed trait T[A]
object T {
case class MkT[A <: String with Singleton](name: A) extends T[A]
}
现在,我想编写一个方法来检查两个T
对象的单例类型参数是否相同,如果是,则以cats.evidence.Is
对象的形式返回该事实的证据案子。
我已经尝试了以下方法,但是不起作用:
import cats.evidence.Is
def checkIs[A,B](ta: T[A],tb: T[B]): Option[Is[A,B]] =
(ta,tb) match {
case (ta: T.MkT[a],tb: T.MkT[b]) if ta.name == tb.name =>
Some(Is.refl[A])
case _ => None
}
// [error] Main.scala:36:75: type mismatch;
// [error] found : cats.evidence.Is[A,A]
// [error] required: cats.evidence.Is[A,B]
如何使编译器相信这是正确的?
//编辑:正如@Dmytro Mitin所指出的那样,进行运行时检查并在编译时说服编译器类型是相同的似乎很矛盾。但这实际上是可行的,并且可以通过更简单的GADT进行演示:
sealed trait SI[A]
object SI {
case object S extends SI[String]
case object I extends SI[Int]
}
def checkInt[A](si: SI[A]): Option[Is[A,Int]] =
si match {
case SI.I => Some(Is.refl[Int])
case _ => None
}
解决方法
使用模式匹配时,您尝试在运行时(T
)检查“两个ta.name == tb.name
对象的单例类型参数是否相同”,但希望在编译时说服编译器。我会尝试类型类
trait CheckIs[A,B] {
def checkIs(ta: T[A],tb: T[B]): Option[Is[A,B]]
}
object CheckIs {
implicit def same[A]: CheckIs[A,A] = (_,_) => Some(Is.refl[A])
implicit def diff[A,B]: CheckIs[A,B] = (_,_) => None
}
def checkIs[A,B](ta: T[A],tb: T[B])(implicit ci: CheckIs[A,B]): Option[Is[A,B]] = ci.checkIs(ta,tb)
checkIs(T.MkT("a"),T.MkT("a")) //Some(cats.evidence.Is$$anon$2@28f67ac7)
checkIs(T.MkT("a"),T.MkT("b")) //None
(顺便说一下,Is
是一个类型类,将其用作隐式约束是很自然的,但是将其用作返回类型有点奇怪)。