问题
Consider this code:
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]])
So far so good. From the definitions above, I concluded, that the expanded type of the result is following:
val itsType: Error[(Int, String), String] = result
But apparently it is not, since the compiler replies with:
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
Is it possible to find out the simplified - expanded type of the expression? I can't get this information from compiler, I tried to print the AST before the erasure phase, but the expanded type is still not there.
回答1:
Firstly, when you write that implicit noneq2
has type TypeOr[E, F]
you lost type refinement https://typelevel.org/blog/2015/07/19/forget-refinement-aux.html . Correct is
implicit def noneq2[E, F](implicit ev: E =:!= F) = new TypeOr[E, F] {
type T = (E, F)
}
or better with explicit type
implicit def noneq2[E, F](implicit ev: E =:!= F): TypeOr[E, F] { type T = (E, F) } = new TypeOr[E, F] {
type T = (E, F)
}
That's the reason why usually type Aux
is introduced
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)
}
}
Secondly, automatically inferred type of result
i.e.Error[TypeOr[Int, String]#T, String]
(type projection TypeOr[Int,String]#T
is a supertype of (y.T forSome { val y: TypeOr[Int, String] })
and moreover of x.T
) is too rough https://typelevel.org/blog/2015/07/23/type-projection.html
It's better to write path-dependent type for result
.
But
val x = implicitly[TypeOr[Int, String]]
val result: Error[x.T, String] =
Err(null.asInstanceOf[Error[Int, Int]]).combine(_ => null.asInstanceOf[Error[String, String]])
doesn't compile.
The thing is that implicitly
can damage type refinements https://typelevel.org/blog/2014/01/18/implicitly_existential.html
That's the reason why there exists macro 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
Alternatively, custom materializer can be defined
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
来源:https://stackoverflow.com/questions/55609380/scala-compiler-expand-types