-
Notifications
You must be signed in to change notification settings - Fork 393
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: Type alias type packs #83
RFC: Type alias type packs #83
Conversation
Also, `type A = X` is no longer allowed when X expects a type pack. Instead, support for `X<>` syntax is proposed. Alternatives section is reworked to talk about explicit type pack syntax and no longer mentions the `type A = X<>` question.
```lua | ||
type A = X -- T... = (). Note: check 'Alternatives' | ||
type A = X<> -- T... = () |
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.
I think that I would prefer to have this as 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.
Should we open a straw poll?
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.
I feel like there's only two consistent syntaxes:
- When specifying type pack arguments, you must specify a type pack explicitly. This means that we don't automatically restructure input sequences, so
X<number, string>
fails when X accepts one type pack parameter - When specifying type pack arguments, and type pack isn't explicitly specified, we take a sequence of types until the last type, or possibly until the next type pack (?), and convert that to a type pack.
In the latter case it feels like X<>
should indeed work and produce an empty type pack parameter.
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.
I had the first option as an alternative in a draft, but I didn't even want to consider that:
One option that we have is to remove implicit pack assignment from a set of types
and a trailing tail and always require new explicit type pack syntax:
type X<T...> = --
type A<S...> = X<S...> -- invalid
type B = X -- invalid
type C = X<number> -- invalid
type D = X<number, string> -- invalid
type E = X<...number> -- invalid
type A<S...> = X<(S...)> -- T... = (S...)
type B = X<()> -- T... = ()
type C = X<(number)> -- T... = (number)
type D = X<(number, string)> -- T... = (number, string)
type E = X<(...number)> -- T... = (...number)
But this doesn't feel natural and for variadic function argument lists, the syntax for
the argument tail is not special.
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.
Types A and E might still work without (), but like I've said it was a draft that I didn't explore.
type Z = Y<number, string, boolean> -- error, Y doesn't have a valid definition | ||
``` | ||
|
||
Splitting off a single type is is a common pattern with variadic templates in C++, but we don't allow type alias overloads, so use cases are more limited. |
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.
I find explicitly representing packs this way to be a surprising direction, but that might just be my familiarity with C++ templates talking. It'd be nice if the rationale for doing it this way was explained a bit more, instead of only how it works mechanically.
It'd still be possible to implement splitting in the future through a magic utility type, right? Something like Select<T..., number>
.
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.
I think it's correct to use 'direction' when talking about something new.
This RFC doesn't select a direction in how type packs works here, this is an existing limitation of the current type system (eager type alias definitions).
If this element of the type system changes (in a different RFC), splitting off type pack elements might become available.
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 would also be great to see useful examples of type pack element extraction in context of Luau programs.
Would this allow me to do the following? As we start looking at strongly-typing dynamic data fetch/mutation, this kind of composability will be more and more important. type void = ()
type tuple<T...> = (T...)
type F<ReturnT> = () -> ReturnT
type voidF = F<()>
type voidF2 = F<void>
type tupleF = F<tuple<string, () -> tuple<number, number>>>
type Obj = { f: voidF, f2: voidF2, useEffect: tupleF }
local obj: Obj = {
f = function() end,
f2 = function() end,
useEffect = function()
return "xyz", function() return 90210, 31337 end
end
} |
No, the code above tries to declare a type pack alias, and this RFC is about generic type pack arguments to type aliases. Type aliases continue to name types, not type packs. |
…assignment to a type argument Added an additional note on type pack element extraction.
This example doesn't look very strong, you would be able to write: type F<ReturnT...> = () -> ReturnT...
type voidF = F<()>
type voidF2 = F<()>
type tupleF = F<(string, () -> (number, number))>
-- code below is the same If there are better examples, that's something outside of this RFC's scope as @zeux mentioned. |
… type pack argument
Updated to remove combining extra types together with the next type pack into a single type pack argument, only types are combined. |
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.
Thanks a lot for thinking this through; I've reread the RFC again and I think this proposal results in something that is easy to think about and use.
Changes: - Support for time tracing for analysis/compiler (not currently exposed through CLI) - Support for type pack arguments in type aliases (#83) - Basic support for require(path) in luau-analyze - Add a lint warning for table.move with 0 index as part of TableOperation lint - Remove last STL dependency from Luau.VM - Minor VS2022 performance tuning Co-authored-by: Rodactor <rodactor@roblox.com>
Changes: - Support for time tracing for analysis/compiler (not currently exposed through CLI) - Support for type pack arguments in type aliases (#83) - Basic support for require(path) in luau-analyze - Add a lint warning for table.move with 0 index as part of TableOperation lint - Remove last STL dependency from Luau.VM - Minor VS2022 performance tuning Co-authored-by: Rodactor <rodactor@roblox.com>
Rendered link