考虑以下代码:
trait TypeOr[E, F] {
type T
}
implicit def noneq2[E, F](implicit ev: E =!= F): TypeOr[E, F] = new TypeOr[E, F] {
type T = (E, F)
}
sealed trait Error[+E, +A]
case class Err[E, A](e: Error[E, A]) {
def combine[B, F](f: A => Error[F, B])(implicit ev: TypeOr[E, F]): Error[ev.T, B] = ???
}
val result = Err(null.asInstanceOf[Error[Int, Int]]).combine(_ => null.asInstanceOf[Error[String, String]])
到现在为止还挺好。根据上面的定义,我得出结论,结果的扩展类型如下:
val itsType: Error[(Int, String), String] = result
但显然它不是,因为编译器回复:
found : returnerror.Comb.Error[returnerror.Comb.TypeOr[Int,String]#T,String]
required: returnerror.Comb.Error[(Int, String),String]
val itsType: Error[(Int, String), String] = result
是否有可能找到表达式的简化扩展类型?我无法从编译器获取此信息,我尝试在擦除阶段之前打印AST,但扩展类型仍然不存在。
首先,当你写隐含的noneq2
有类型TypeOr[E, F]
时,你丢失了类型细化https://typelevel.org/blog/2015/07/19/forget-refinement-aux.html。正确的是
implicit def noneq2[E, F](implicit ev: E =:!= F) = new TypeOr[E, F] {
type T = (E, F)
}
或者更好的显式类型
implicit def noneq2[E, F](implicit ev: E =:!= F): TypeOr[E, F] { type T = (E, F) } = new TypeOr[E, F] {
type T = (E, F)
}
这就是为什么通常会引入类型Aux
的原因
object TypeOr {
type Aux[E, F, T0] = TypeOr[E, F] { type T = T0 }
implicit def noneq2[E, F](implicit ev: E =:!= F): Aux[E, F, (E, F)] = new TypeOr[E, F] {
type T = (E, F)
}
}
其次,自动推断类型的result
ieError[TypeOr[Int, String]#T, String]
(类型投影TypeOr[Int,String]#T
是(y.T forSome { val y: TypeOr[Int, String] })
的超类型,而且x.T
)太粗糙https://typelevel.org/blog/2015/07/23/type-projection.html
最好为result
编写路径依赖类型。
但
val x = implicitly[TypeOr[Int, String]]
val result: Error[x.T, String] =
Err(null.asInstanceOf[Error[Int, Int]]).combine(_ => null.asInstanceOf[Error[String, String]])
不编译。
问题是implicitly
可以破坏类型改进https://typelevel.org/blog/2014/01/18/implicitly_existential.html
这就是存在宏观shapeless.the
的原因。
val x = the[TypeOr[Int, String]]
val result: Error[x.T, String] = Err(null.asInstanceOf[Error[Int, Int]]).combine(_ => null.asInstanceOf[Error[String, String]])
val itsType: Error[(Int, String), String] = result
或者,可以定义自定义材料化器
object TypeOr {
//...
def apply[E, F](implicit typeOr: TypeOr[E, F]): Aux[E, F, typeOr.T] = typeOr
}
val x = TypeOr[Int, String]
val result: Error[x.T, String] =
Err(null.asInstanceOf[Error[Int, Int]]).combine(_ => null.asInstanceOf[Error[String, String]])
val itsType: Error[(Int, String), String] = result