Description
I've just run across the following limitation of inline given
instances as compared with inline implicit
defs ...
shapeless's Annotations
type class is of the form,
trait Annotations[A, T] {
type Out <: Tuple
def apply(): Out
}
Given an annotation Annot
and a case class CC
,
case class Annot() extends scala.annotation.Annotation
case class C(i: Int, @Annot s: String)
the instances Annotations[Annot, CC]
is a term of the form,
new Annotations[Annot, CC] {
type Out = (None.type, Some[Annot])
def apply(): Out = (None, Some(Annot()))
}
This term is materialized via a Dotty macro.
Ideally we would like to be able to provide the instance as a given
,
inline given mkAnnotations[A, T] as Annotations[A, T] =
${ AnnotationMacros.mkAnnotations[A, T] }
However, this doesn't work because the the result type of the given
, Annotations[A, T]
, omits the refinement { type Out = ... }
. We can't provide the refinement explicitly because it's computed by the macro expansion on the RHS.
We can provide the instance as an inline implicit
def, however, using specializing inline,
inline implicit def mkAnnotations[A, T] <: Annotations[A, T] =
${ AnnotationMacros.mkAnnotations[A, T] }
Here the result type is refined to be as precise as the expansion on the RHS. The reason we can't do this in the given
case is because we have no way to indicate that we want the refined type rather than the explicitly annotated type.
This is going to be an issue for any type class which computes both a type and a term as is done by shapeless.Annotations
.
At least while we continue to have implicit
defs and specializing inline we have a mechanism to do what needs to be done here, but it would be nice to be able to express this using givens
, if only for syntactic consistency.