RFC: Name support for all Type Pack annotations#206
Conversation
|
That's a good idea, but I think this syntax would more idiomatic for Luau: type Foo<T...> = (T...) -> ()
type a = Foo<x: number, y: number, z: number> |
|
I'm fairly certain the thing you actually want is for us to allow plain type packs (i.e. not in function parameters) to contain names, e.g. |
|
How would this interact with, say type A<T> = { foo: T }
A<[bar: number]>? do we end up with a field named both |
If it would work, that would be cool too! Just a question about whether this would be allowed in the future type a = x: number |
Are type packs, things that are within e.g. if so, then yeah I thought type packs are only when it is more than one type, e.g. is would writing |
So, in this case |
No, you can write both types and type packs within angle brackets because you can have both type parameters and type pack parameters. Type packs are just "any collection of zero or more types." The things on either side of the The syntax of Luau today doesn't currently allow you to write those type packs with names, which is what I would expect this RFC to actually be proposing, but you can write type packs in general, e.g. type TakesAType<T> = { T }
type Instantiated = TakesAType<number>
type TakesATypePack<T...> = (T...) -> ()
type Instantiated2 = TakesATypePack<(number, string)>Luau today does some context-aware determination for what is what, so |
Editing. I assume type packs count as one type? Wondering what would happen in this case as well: In the event where an annotation already has a name. The name would get overwritten if... type Foo<T...> = (args: T...) -> ()
type Foo_2<T> = (arg: T) -> ()
type A = Foo<(number, number)> -- nothing changes about "args:"
type A = Foo<(x: number, y: number)> -- "args" would be gone if something substituted a generic
type C = Foo_2<(x: number)> -- same here for "arg" |
The first example wouldn't work, since you can't currently give a name to the tail of your parameter list. type Foo<T...> = (T...) -> ()
type C = Foo<(x: number)> |
This makes me think that this syntax should be allowed: |
Base is these syntax work type E = (number)
type E2 = (number) -> ()and RFC proposes this type E = (x: number)
type E2 = (x: number) -> () -- already works no changes!so all other |
At least from what I understand: type Fn<T> = (T) -> () -- T here isn't actually a type pack
type A = Fn<(number)> -- Even here, T still isn't a type pack, and something like Fn<(...number)> would be an errorSo what you're actually proposing is that every type (not just type packs) can have an optional name, but I'm not sure if that is necessarily useful, and you'd need to decide what happens when you give a name to a type that already has a name: type Fn<T> = (arg: T) -> ()
type B = Fn<(x: number)> -- Is this (arg: number) or (x: number)?You don't have this ambiguity if you're only working with type packs, because type packs can't be assigned names, like I said earlier. type Fn<T...> = (arg: T...) -> () -- This isn't something you can do at all |
Type packs and types are just... different classes of thing. One type pack is one type pack. One type pack is never one type, but one type pack might consist of one type. |
I think that would probably be the most consistent thing, for sure, but I'm not sure the grammar would hold up if we had actually allowed the parameter type pack to be unparenthesized. |
This here does not error in https://play.luau.org/ --!strict
type E<T> = T
local e: E<(number)>
type E2<T> = (T) -> ()
local e2: E2<(number)> |
|
Yes, that doesn't error, but no type packs are involved anywhere in that code. You have |
Ah. Well, I guess the most logical thing for this RFC then, is to leave, as type Fn<T> = (arg: T) -> ()As it is unclear for now, what should happen for this, and a different RFC could tackle it. 🤔 |
What if it was type E<T> = (T)
local e: E<(x: number)>would that work? oh ok, I think I see it wouldn't work, because And |
Not necessarily, it depends on what is being instantiated, for example: type Example<T, U> = (T, U) -> ()
-- T becomes number and U becomes string, both are types
-- (number, string) -> ()
type A = Example<number, string>
type ExamplePack<T...> = (T...) -> ()
-- T... is a type pack
-- (number, string) -> ()
type B = ExamplePack<number, string>Both |
|
tried to update the RFC this would be what would remain unsupported We can't rename type Foo<T> = (arg: T) -> ()
type A = Foo<(x: number)> -- (x: number), does not count as a type pack
-- Not possible!
type Bar<T,U> = (T,U) -> ()
local e: Bar<(x: number, y: number)> |
|
I think the design space here needs to be organized a bit more here, because this feels more like a lot of interesting ideas that just aren't unified yet. First, there needs to be some definition for what a "type pack" actually is. Obviously, type Fn1<T> = (T) -> ()
type A = Fn1<(x: number)> -- not a type pack, so this is wrong
type Fn2<T...> = (T...) -> ()
type B = Fn2<(x: number)> -- but this is!Second, the return thing of function types needs to be formalized as a type pack as well, so With these definitions, it becomes clear what is and isn't allowed:
|
Rendered