Skip to content
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

RFC: Sealed table subtyping #38

Merged
merged 1 commit into from
May 31, 2021

Conversation

AmaranthineCodices
Copy link
Contributor

@AmaranthineCodices AmaranthineCodices added the rfc Language change proposal label May 17, 2021
@zeux
Copy link
Collaborator

zeux commented May 17, 2021

Alternative syntax for tables open for extension: ... postfix inside the table (similarly to runtime syntax for varargs).

@zeux
Copy link
Collaborator

zeux commented May 17, 2021

What do we infer as the type of

function foo(t)
return t.field
end

right now?

@andyfriesen
Copy link
Collaborator

We don't have surface syntax for generic tables, but if we did, it would be something like

foo : <T>{field: T, ...} -> T

@zeux
Copy link
Collaborator

zeux commented May 17, 2021

Aha - generic tables is what I was looking for. After this change, would sealed and generic tables be different?

Copy link
Contributor

@tiffany352 tiffany352 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does going with unsealed as the default for table types imply we'll need to add a syntax for sealed tables later? Sort of like the inverse of the interface {} alternative.

It seems like this proposal will make it impossible to declare sealed tables except via typeof.

@AmaranthineCodices
Copy link
Contributor Author

@zeux

Aha - generic tables is what I was looking for. After this change, would sealed and generic tables be different?

Generic tables will stick around for a bit longer. Constrained generics will probably replace the generic table state entirely, but for right now they have some subtle behavioral differences that we want to keep around.

@tiffany352

Does going with unsealed as the default for table types imply we'll need to add a syntax for sealed tables later? Sort of like the inverse of the interface {} alternative.

It seems like this proposal will make it impossible to declare sealed tables except via typeof.

This proposal doesn't change the semantics of sealed tables; they will still occur in the same places. Unsealed tables are sealed when they leave the scope they were created in, so e.g. this function returns a sealed table, not an unsealed one:

local function x()
    return {
        a = 1,
    }
end

This subtyping mechanic is actually unsound for unsealed tables without flow analysis:

local x = {
    a = 1,
}

local y = {
    a = 1,
    b = 2,
}

-- both x and y are unsealed in this scope

local z: typeof(x) = y -- y is said to be a subtype of x for this to work
x.c = 3 -- now x has a "c" property, which y does not. accessing z.c is nil.

This introduces some somewhat awkward semantics for cases where type annotations aren't involved, unfortunately. Using type annotations can force a table to be sealed; for example, in this code:

type Foo = {
    a: number,
}

local x: Foo = {
    a = 123,
}

x is a sealed table, not an unsealed one.

@asajeffrey
Copy link
Collaborator

At some point we'd want to replace generic tables b bounds on type parameters, e.g.

function foo(x)
  x.p = 5
  return x
end

would get inferred as having type

foo : <t : {p : number}>(t) -> t

Copy link
Collaborator

@asajeffrey asajeffrey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@zeux
Copy link
Collaborator

zeux commented May 28, 2021

I feel like long term this can make the type system easier to understand if we stop using the words "sealed table"; instead this purely becomes a table, generic tables go away in lieu of generic constraints, and unsealed tables are just a transient state that happens during type checking. Short term we should consider rewording the manual (and error messages if we have any) to refer to sealed tables as tables while keeping the other types...

@zeux zeux merged commit 43d606f into luau-lang:master May 31, 2021
@AmaranthineCodices AmaranthineCodices deleted the rfc-sealed-subtyping branch July 8, 2021 20:26
@andyfriesen
Copy link
Collaborator

One thought I had was that we could just convert sealed tables in function arguments to generic tables during quantification. I think this would mostly get us what we want with minimal changes.

It seems to be very rare that anyone ever actually wants a sealed table as an argument.

If we ever actually do run into that use case, we could do what Flow does and offer explicit syntax for a sealed table type.

@aatxe aatxe mentioned this pull request Aug 4, 2022
@aatxe aatxe mentioned this pull request Jan 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
rfc Language change proposal
Development

Successfully merging this pull request may close these issues.

7 participants