Skip to content

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

Merged
merged 3 commits into from
Oct 22, 2020

Conversation

nicolasstucki
Copy link
Contributor

@nicolasstucki nicolasstucki commented Oct 20, 2020

Current state

In general, instances of scala.quoted.Type are and should not be used explicitly in quotes.

+ 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.

??? 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.

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.

??? 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:

def f[T](x: T)(using Type[T])(using QuoteContext) = 
  '{ val y: T = $x; ... }
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.

@nicolasstucki nicolasstucki self-assigned this Oct 20, 2020
@nicolasstucki
Copy link
Contributor Author

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.

@nicolasstucki nicolasstucki force-pushed the quoted-type-pattern branch 2 times, most recently from 9748734 to 6d329ca Compare October 20, 2020 09:35
@nicolasstucki nicolasstucki changed the title Make type of type hole available quoted patterns Make type of type hole available in quoted patterns Oct 20, 2020
@smarter

This comment has been minimized.

@nicolasstucki

This comment has been minimized.

@nicolasstucki

This comment has been minimized.

@smarter

This comment has been minimized.

@nicolasstucki

This comment has been minimized.

@nicolasstucki
Copy link
Contributor Author

The syntax discussion is out of the topic of this PR. We should have it on a proper issue. I will create one later.

@nicolasstucki
Copy link
Contributor Author

I created #10050 to follow up the syntax discussions

Copy link
Contributor

@liufengyun liufengyun left a 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)
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor Author

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.

Copy link
Contributor Author

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
Copy link
Contributor

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.

Copy link
Contributor Author

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.
Copy link
Contributor

@liufengyun liufengyun left a 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)
Copy link
Contributor

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 👍

Copy link
Contributor Author

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.

@nicolasstucki nicolasstucki merged commit a3c3396 into scala:master Oct 22, 2020
@nicolasstucki nicolasstucki deleted the quoted-type-pattern branch October 22, 2020 08:00
nicolasstucki added a commit to dotty-staging/dotty that referenced this pull request Oct 27, 2020
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.
@Kordyjan Kordyjan added this to the 3.0.0 milestone Aug 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants