-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Make quoted.Type fully contextual #10207
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make quoted.Type fully contextual #10207
Conversation
839901d
to
8691aae
Compare
object TypeTree extends TypeTreeModule | ||
object TypeTree extends TypeTreeModule: | ||
def of[T <: AnyKind](using tp: scala.quoted.Type[T]): TypeTree = | ||
tp.asInstanceOf[scala.internal.quoted.Type[TypeTree]].typeTree |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is that cast needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More precisely, why is it Type[TypeTree]
and not Type[T]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because it is the scala.internal.quoted.Type
and not scala.quoted.Type
. That variant is parametrised with the tree type to make it easier to cast and extract internally.
@@ -5,8 +5,6 @@ import scala.annotation.compileTimeOnly | |||
abstract class Type[T <: AnyKind] private[scala]: | |||
type Underlying = T | |||
|
|||
def unseal(using qctx: QuoteContext): qctx.reflect.TypeTree |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From the tests, it seems this method is more convenient and shorter than the proposed alternative. Conceptually, it also helps reinforce the boundary between the safer quoted world and the unsafe reflect world. In addition, the method is also symmetric to the Expr[T]
variant, which is good for usability.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea is that the Type
API should be symmetric with the Type
/TypeRepr
/TypeTree
. As Type
does not share the level symmetry of Expr
, Type
should not try to have an API that reflects the level symmetry as Expr
does. Usability wise, this symmetry between the APIs has been the source of confusion of what and how Type
works.
The most common use case is to inspect the type for which Type[A].unseal.tpe
becomes TypeRepr.of[T]
which is shorter and much clearer as it states exactly what we want to get for this T
.
There are some tests that used to give a name to the Type[T]
and then call t.unseal.tpe
which was slightly shorter at the cost of a summon[Type[T]]
or using [T](using t: Type[T])
instead of [T: Type]
which made code larger before the .unseal
call.
Another classical mistake is to start with [T](...)(t: Type[T])
and call t.unseal.tpe
and then realize that somewhere else it is needed implicitly and modify the signature to [T: Type](....)(t: Type[T])
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see in #10232 Type.seal
is changed to asType
, which is consistent with the asymmetry you mentioned above. I think it's fine as long as the whole design is consistent.
One ergonomics issue (might just be personal preference) is that I find dot-style API (tp.unseal.tpe
) is more friendly than bureaucratic-style API (TypeRepr.of(tp)
). However, I do see the consistency of the overall APIs is more important. So feel free to choose the one that makes more sense overall.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Type is designed to be a contextual value. As such we should be able to use it without needing to name it and use it explicitly. Now that quoted pattterns provide the name of the type we can do this. * Replaced methods `show`/`showAnsiColored` by equivalent methods in `object Type` * Remove `unseal`, it can be replaced directly with `TypeRepr.of` in most cases __Migration__ * `Type[T].show` -> `Type.show[T]` * `Type[T].unseal.tpe` -> `TypeRepr.of[T]` * `Type[T].unseal` -> `TypeTree.of[T]`
8691aae
to
4d33dbf
Compare
Rebased and resolved a trivial conflict |
Type is designed to be a contextual value. As such we should be able to use it without needing to name it and use it explicitly. Now that quoted pattterns provide the name of the type we can do this.
show
/showAnsiColored
by equivalent methods inobject Type
unseal
, it can be replaced directly withTypeRepr.of
in most casesMigration
Type[T].show
->Type.show[T]
Type[T].unseal.tpe
->TypeRepr.of[T]
Type[T].unseal
->TypeTree.of[T]