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: Type alias type packs #83

Merged
merged 4 commits into from
Oct 27, 2021

Conversation

vegorov-rbx
Copy link
Collaborator

@vegorov-rbx vegorov-rbx commented Oct 6, 2021

@vegorov-rbx vegorov-rbx added the rfc Language change proposal label Oct 6, 2021
@vegorov-rbx vegorov-rbx marked this pull request as draft October 6, 2021 19:00
@vegorov-rbx vegorov-rbx marked this pull request as ready for review October 6, 2021 19:00
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... = ()
Copy link
Contributor

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<()>.

Copy link
Collaborator Author

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?

Copy link
Collaborator

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.

Copy link
Collaborator Author

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.

Copy link
Collaborator Author

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.

rfcs/syntax-type-alias-type-packs.md Outdated Show resolved Hide resolved
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.
Copy link
Contributor

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

Copy link
Collaborator Author

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.

Copy link
Collaborator Author

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.

@matthargett
Copy link

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
}

@zeux
Copy link
Collaborator

zeux commented Oct 14, 2021

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.
@vegorov-rbx
Copy link
Collaborator Author

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.

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.

@vegorov-rbx
Copy link
Collaborator Author

Updated to remove combining extra types together with the next type pack into a single type pack argument, only types are combined.
Also added an alternative about using only explicit type packs.

Copy link
Collaborator

@zeux zeux left a 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.

@zeux zeux merged commit 0fd38fd into luau-lang:master Oct 27, 2021
zeux added a commit that referenced this pull request Nov 5, 2021
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>
zeux added a commit that referenced this pull request Nov 5, 2021
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>
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.

6 participants