Skip to content

Decouple quoted Type interface from encoding #9485

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

Conversation

nicolasstucki
Copy link
Contributor

Before, quoted.Type was defined as

// old
class Type[X <: AnyKind]:
  type T = X

Where T is the proper type of the instance of a Type. This is the type used for all operations within the compiler.

The only purpose of the type X was to be able to write context bounds as [X: Type] and staged types Type[X] instead of
Type { type T = X }.
Unfortunately, the type X makes the assumption that X is known, which implies that instances of Type with unknown types need to be encoded with a whildcard type.
A val t: Type[?] = ... is unusable as we cannot heal staged wildcard types, even though we do have a t.T which can be handled.

Now, we change the definition of Type to only have the type member.

// new
class Type:
  type T <: AnyKind

This solves the aforementioned limitations with unkown staged types.
Now we are also able to remove the wildcard from QuoteContext.tasty.Type.seal and the result can be used in quotes.

To keep the context bound syntax and provide more descriptive name for the concept, we defined the quoted.Staged type alias.

type Staged[X <: AnyKind] = Type { type T = X }

@nicolasstucki nicolasstucki self-assigned this Aug 3, 2020
Before, `quoted.Type` was defined as

```scala
// old
class Type[X <: AnyKind]:
  type T = X
```

Where `T` is the proper type of the instance of a `Type`. This is the type used for all operations within the compiler.

The only purpose of the type `X` was to be able to write context bounds as `[X: Type]` and staged types `Type[X]` instead of
`Type { type T = X }`.
Unfortunately, the type `X` makes the assumption that `X` is known, which implies that instances of `Type` with unknown types need to be encoded with a whildcard type.
A `val t: Type[?] = ...` is unusable as we cannot heal staged wildcard types, even though we do have a `t.T` which can be handled.

Now, we change the definition of `Type` to only have the type member.
```scala
// new
class Type:
  type T <: AnyKind
```

This solves the aforementioned limitations with unkown staged types.
Now we are also able to remove the wildcard from `QuoteContext.tasty.Type.seal` and the result can be used in quotes.

To keep the context bound syntax and provide more descriptive name for the concept, we defined the `quoted.Staged` type alias.

```scala
type Staged[X <: AnyKind] = Type { type T = X }
```
@nicolasstucki nicolasstucki force-pushed the decouple-quoted-type-interface-from-encoding branch from fa59dd9 to d488d31 Compare August 3, 2020 12:13
@nicolasstucki
Copy link
Contributor Author

This changes the tests for #6997 and #8871 from negative to positive

@nicolasstucki
Copy link
Contributor Author

@liufengyun could you have a look at this PR? Only the community build fails, I will update it once we settled on the design/names.

@therealcisse
Copy link
Contributor

I propose to name this Type.Aux instead of Staged 😎

@liufengyun
Copy link
Contributor

A val t: Type[?] = ... is unusable as we cannot heal staged wildcard types, even though we do have a t.T which can be handled.

Could you produce a simple example where the current scheme does not work, and how it works after the change?

@nicolasstucki
Copy link
Contributor Author

For example when we seal a tasty.Type we get a quoted.Type[?] (which we can define to quoted.Type[? <: Any] to remove the AnyKindness). Currently it is impossible to splice this type without jumping through hoops by crafting a local type for it and using unsafe casts.

// Current
import scala.quoted._
def f(using t: Type[? <: Any])(using QuoteContext): Unit = '{
  (x: $t) => ???
// |   ^
// |   Cannot splice t.T because it is a wildcard type
}
// With this PR
import scala.quoted._
def f1(using t: Staged[? <: Any])(using QuoteContext): Unit = '{
  (x: $t) => ??? // ok
}
// or the equivalent
def f2(using t: Type { type T <: Any } )(using QuoteContext): Unit = '{
  (x: $t) => ??? // ok
}

Note: there is an orthogonal improvement that can be done to avoid the extra <: Any bounds.

@liufengyun
Copy link
Contributor

We are exposing a new concept Staged[T] in addition to Type, which complicates the programming interface for common use cases. What about using the name Type[T] for the common use cases?

BTW, technically it seems we are trying to find an encoding for the absence of existential types.
Can we play with the return type of seal to achieve the same effect?

trait SomeType[U] {
  type T <: U
  val T: Type[T]
}

def f1(t: SomeType[U])(using QuoteContext): Unit = '{
  (x: ${t.T}) => ??? // ok
}

@liufengyun
Copy link
Contributor

liufengyun commented Aug 6, 2020

Or, even simpler, what about changing the type signature of seal to the following:

def seal[T]: Expr[T] = ...

Is it enough for use cases that this PR tries to support?

@nicolasstucki
Copy link
Contributor Author

nicolasstucki commented Aug 6, 2020

The SomeType idea is just a clunky workaround. It does no solve the core issue and is harder to use it usually needs to be placed in a stable binding. It is exactly the workaround I used for now in #8940R1765 which made me realize the need to clean up the core rather than add ad-hoc patches on top.

On the other side I want to avoid using Type for the alias as in the future, there is the possibility of merging quoted.Type and tasty.Type. If we allow Type[T] then we would fall back into the same ambiguity we have right now. Where Type { type T = ... } is a valid refinement either way.

@nicolasstucki
Copy link
Contributor Author

Another argument to avoid Type is to have a meaningful name and avoid descriptions like a type Type of type ... .

@nicolasstucki
Copy link
Contributor Author

@liufengyun, not sure what this means:

def seal[T]: Expr[T] = ...

Maybe you meant

def seal[T]: Type[T] = ...

if so, this does not work because we do not have the type T in the first place.

@liufengyun
Copy link
Contributor

Maybe you meant

def seal[T]: Type[T] = ...

if so, this does not work because we do not have the type T in the first place.

Yes, I mean exactly that. I'm wondering why we need the existential T. Can it just be Any in the use cases?

I feel Staged[T] is not a great name, compared to Type[T]. It would be nice to use Type[T] for common cases.

@nicolasstucki
Copy link
Contributor Author

But Type is not descriptive at all as T in [T: Type] is obviously a type. We need some name that what this type will be used for or what kind of operations we have on it.

@nicolasstucki nicolasstucki force-pushed the decouple-quoted-type-interface-from-encoding branch from 0e27489 to 57cc41a Compare August 11, 2020 15:31
@nicolasstucki nicolasstucki removed the request for review from odersky August 11, 2020 15:32
@nicolasstucki nicolasstucki force-pushed the decouple-quoted-type-interface-from-encoding branch from 57cc41a to d488d31 Compare August 11, 2020 15:34
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