-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Make type of type hole available in quoted patterns #10045
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 type of type hole available in quoted patterns #10045
Conversation
Note that this was the original design but we went the other way due to technical challenges while typing the quoted patterns. Now I see that it was actually not that complicated to implement this, just had to use semantic names correctly. |
9748734
to
6d329ca
Compare
6d329ca
to
3f038b4
Compare
This comment has been minimized.
This comment has been minimized.
3f038b4
to
1fd6034
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
The syntax discussion is out of the topic of this PR. We should have it on a proper issue. I will create one later. |
1fd6034
to
b3e09f9
Compare
I created #10050 to follow up the syntax discussions |
b3e09f9
to
3cc348e
Compare
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.
Otherwise, LGTM
&& (n != true_) | ||
&& (n != null_)) | ||
} | ||
} || name.is(PatMatQuoteTypeEv) |
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 this change is needed? This kind of specialization is not scalable.
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.
For a $T
we need to create a name while typing for the given Type[T]
that will be available if the pattern matched. We need to use a semantic name to make sure that this name does not leak into userspace. This change makes this particular kind of semantic name be a valid pattern var to make typer handle it as such.
I don't see any other case where we would use something like this and therefore I'm not sure we need it to scale. If we have another use-case in the future we can revise this and create a more general mechanism. We could achieve the same if we use an attachment on the Ident
to mark such trees but this would come with some extra runtime overhead.
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.
Looking at it, PatMatQuoteTypeEv
is not needed for anything specific quote wise. I can rename to VarPattern
or something like that.
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.
Now it scales to any other synthetic var pattern.
'{ | ||
val tmp: $t = $x | ||
val tmp: T = $x |
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.
This is not as symmetric as before. I think meta-programmers will be at a loss about whether to use $
or not before a type.
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.
It is not symmetric because it is not intended to be as quoted types do not follow the same level symmetry that expression do. The syntax $
for types won't exist outside of patterns and hence they will have a single option and won't be able to get confused about this at the use site. The use of $
in the pattern is its own issue (see #10050) which we will address separately. Here we focus on the use sites and keep the changes to the patterns to a minimum.
In general, instances of `scala.quoted.Type` are and should not be used explicitly in quotes. ```diff + def f[T](x: T)(using Type[T])(using QuoteContext) = + '{ val y: T = $x; ... } // good code - def f[T](x: T)(using t: Type[T])(using QuoteContext) = - '{ val y: t.T = $x; ... } // bad code - def f[T](x: T)(using t: Type[T])(using QuoteContext) = - '{ val y: $t = $x; ... } // bad code (syntax will be removed) ``` The only exception is on types extracted using quoted patterns as these hide tier type inside an instance of `Type[T]` which must be accessed using it's path. ```diff ??? match case '{ $x: $t } => - '{ val y: t.T = $x; ... } // bad code - '{ val y: $t = $x; ... } // bad code (syntax will be removed) ``` Here `t` is provided as a `given` as if we refer to its type we must be able to summon it. The change is to provide the name of the type that is extracted rather than the `given Type[T]`. `Type[T]` can be summoned were needed as in all other uses of abstract types in quotes. ```diff ??? match case '{ $x: $T } => + '{ val y: T = $x; ... } // good code - val t: Type[T] = summon[Type[T]] - '{ val y: t.T = $x; ... } // bad code - '{ val y: $t = $x; ... } // bad code (syntax will be removed) ``` The pattern will make `T` and a `given Type[T]` available on the right-hand side of the pattern. The `T` can be used the same way it could be used in the implementation of a method with signature: ```scala def f[T](x: T)(using Type[T])(using QuoteContext) = '{ val y: T = $x; ... } ``` ```scala case '{ $x: $T } => '{ val y: T = $x; ... } ``` It is also possible to use lower cases for the type splices `case '{ $x: $t } => '{ val y: t = $x; ... }`. This is similar to the use of type variables in normal patterns `case Some[t](x) =>`. It is preferable to use upper cases as it makes it clearer that this is a type and renders better on the uses in the right-hand side of the pattern.
3cc348e
to
12764e2
Compare
ee86bbc
to
7bc0173
Compare
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
&& (n != true_) | ||
&& (n != null_)) | ||
} | ||
} || name.is(PatMatVarName) |
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 renaming makes everything much more clear 👍
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.
Indeed. The scalability concern made me realize that it was just a misnomer.
After scala#10045 we do not have any cases where we need to use this type reference explicitly in the source code. We rename it to have a descriptive name to improve error messages and to push users to use the canonical representation of types and never use them explicilty.
Current state
In general, instances of
scala.quoted.Type
are and should not be used explicitly in quotes.The only exception is on types extracted using quoted patterns as these hide tier type inside an instance of
Type[T]
which must be accessed using it's path.Here
t
is provided as agiven
as if we refer to its type we must be able to summon it.Changes
The change is to provide the name of the type that is extracted rather than the
given Type[T]
.Type[T]
can be summoned were needed as in all other uses of abstract types in quotes.The pattern will make
T
and agiven Type[T]
available on the right-hand side of the pattern.The
T
can be used the same way it could be used in the implementation of a method with signature:It is also possible to use lower cases for the type splices
case '{ $x: $t } => '{ val y: t = $x; ... }
.This is similar to the use of type variables in normal patterns
case Some[t](x) =>
.It is preferable to use upper cases as it makes it clearer that this is a type and renders better on the uses in the right-hand side of the pattern.