From 4b02be4e0e1e5488cedfaf250df8dd9f5dc63351 Mon Sep 17 00:00:00 2001 From: Alan Jeffrey <403333+asajeffrey@users.noreply.github.com> Date: Wed, 29 Sep 2021 15:05:14 -0500 Subject: [PATCH] September 2021 Luau Recap (#81) * Added the September Luau Recap Co-authored-by: Arseny Kapoulkine --- .../2021-09-30-luau-recap-september-2021.md | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 docs/_posts/2021-09-30-luau-recap-september-2021.md diff --git a/docs/_posts/2021-09-30-luau-recap-september-2021.md b/docs/_posts/2021-09-30-luau-recap-september-2021.md new file mode 100644 index 0000000..87793eb --- /dev/null +++ b/docs/_posts/2021-09-30-luau-recap-september-2021.md @@ -0,0 +1,126 @@ +--- +layout: single +title: "Luau Recap: September 2021" +--- + +Luau is our new language that you can read more about at [https://luau-lang.org](https://luau-lang.org). + +[Cross-posted to the [Roblox Developer Forum](https://devforum.roblox.com/t/luau-recap-september-2021/).] + +## Generic functions + +The big news this month is that generic functions are back! + +Luau has always supported type inference for generic functions, for example: +```lua +type Point = { x: X, y: Y } +function swap(p) + return { x = p.y, y = p.x } +end +local p : Point = swap({ x = "hi", y = 37 }) +local q : Point = swap({ x = "hi", y = true }) +``` +but up until now, there's been no way to write the type of `swap`, since Luau didn't have type parameters to functions (just regular old data parameters). Well, now you can: +```lua +function swap(p : Point): Point + return { x = p.y, y = p.x } +end +``` +Generic functions can be used in function declarations, and function types too, for example +```lua +type Swapper = { swap : (Point) -> Point } +``` + +People may remember that back in +[April](https://devforum.roblox.com/t/luau-recap-april-2021/) we +announced generic functions, but then had to disable them. That was +because [DataBrain](https://devforum.roblox.com/u/databrain) discovered a [nasty +interaction](https://devforum.roblox.com/t/recent-type-system-regressions-for-generic-parametered-functions/) +between `typeof` and generics, which meant that it was possible to +write code that needed nested generic functions, which weren't +supported back then. + +Well, now we do support nested generic functions, so you can write code like +```lua +function mkPoint(x) + return function(y) + return { x = x, y = y } + end +end +``` +and have Luau infer a type where a generic function returns a generic function +```lua +function mkPoint(x : X) : (Y) -> Point + return function(y : Y) : Point + return { x = x, y = y } + end +end +``` +For people who like jargon, Luau now supports *Rank N Types*, where +previously it only supported Rank 1 Types. + +## Bidirectional typechecking + +Up until now, Luau has used *bottom-up* typechecking. For example, for +a function call `f(x)` we first find the type of `f` (say it's +`(T)->U`) and the type for `x` (say it's `V`), make sure that `V` is +a subtype of `T`, so the type of `f(x)` is `U`. + +This works in many cases, but has problems with examples like registering +callback event handlers. In code like +```lua +part.Touched:Connect(function (other) ... end) +``` +if we try to typecheck this bottom-up, we have a problem because +we don't know the type of `other` when we typecheck the body of the function. + +What we want in this case is a mix of bottom-up and *top-down* typechecking. +In this case, from the type of `part.Touched:Connect` we know that `other` must +have type `BasePart`. + +This mix of top-down and bottom-up typechecking is called +*bidirectional typechecking*, and means that tools like type-directed +autocomplete can provide better suggestions. + +## Editor features + +We have made some improvements to the Luau-powered autocomplete beta feature in Roblox Studio: + + * We no longer give autocomplete suggestions for client-only APIs in server-side scripts, + or vice versa. + * For table literals with known shape, we provide autocomplete suggestions for properties. + * We provide autocomplete suggestions for `Player.PlayerGui`. + * Keywords such as `then` and `else` are autocompleted better. + * Autocompletion is disabled inside a comment span (a comment starting `--[[`). + +## Typechecking improvements + +In other typechecking news: + + * The Luau constraint resolver can now refine the operands of equality expressions. + * Luau type guard refinements now support more arbitrary cases, for instance `typeof(foo) ~= "Instance"` eliminates anything not a subclass of `Instance`. + * We fixed some crashes caused by use-after-free during type inference. + * We do a better job of tracking updates when script is moved inside the data model. + * We fixed one of the ways that [recursive types could cause free types to leak](https://devforum.roblox.com/t/free-types-leaked-into-this-modules-public-interface/1459070). + * We improved the way that `return` statements interact with mutually recursive + function declarations. + * We improved parser recovery from code which looks like a function call (but isn't) such as +```lua +local x = y +(expr)[smth] = z +``` + * We consistently report parse errors before type errors. + * We display more types as `*unknown*` rather than as an internal type name like `error####`. + * Luau now infers the result of `Instance:Clone()` much more accurately. + +## Performance improvements + + * `Vector3.new` constructor has been optimized and is now ~2x faster + * A previously implemented optimization for table size prediction has been enhanced to predict final table size when `setmetatable` is used, such as `local self = setmetatable({}, Klass)` + * Method calls for user-specified objects have been optimized and are now 2-4% faster + * `debug.traceback` is now 1.5x faster, although `debug.info` is likely still superior for performance-conscious code + * Creating table literals with explicit numeric indices, such as `{ [1] = 42 }`, is now noticeably faster, although list-style construction is still recommended. + +## Other improvements + + * The existing 'TableLiteral' lint now flags cases when table literals have duplicate numeric indices, such as `{ [1] = 1, [1] = 2 }`