Luau Recap: November 2021

Welcome to the Luau Recap! Luau is our new language that you can read more about at https://luau-lang.org.

Type packs in type aliases

Type packs are the construct Luau uses to represent a sequence of types. We’ve had syntax for generic type packs for a while now, and it sees use in generic functions, but it hasn’t been available in type aliases. That has changed, and it is now syntactically legal to write the following type alias:


type X<A...> = () -> A...

type Y = X<number, string>

We’ve also added support for explicit type packs. Previously, it was impossible to instantiate a generic with two or more type pack parameters, because it wasn’t clear where the first pack ended and the second one began. We have introduced a new syntax for this use case:


type Fn<P..., R...> = (P...) -> R...

type X = Fn<(number, string), (string, number)>

For more information, check out the documentation or the RFC for this feature.

Luau is open-source!

We announced this in early November but it deserves repeating: Luau is now an open-source project! You can use Luau outside of Roblox, subject to MIT License, and - importantly - we accept contributions.

Many changes contributed by community, both Roblox and external, have been merged since we’ve made Luau open source. Of note are two visible changes that shipped on Roblox platform:

  • The type error “Expected to return X values, but Y values are returned here” actually had X and Y swapped! This is now fixed.

  • Luau compiler dutifully computed the length of the string when using # operator on a string literal; this is now fixed and #"foo" compiles to 3.

You might think that C++ is a scary language and you can’t contribute to Luau. If so, you’d be happy to know that the contents of https://luau-lang.org, where we host our documentation, is also hosted on GitHub in the same repository (luau/docs at master · Roblox/luau · GitHub) and that we’d love the community to contribute improvements to documentation among other changes! For example see issues in this list that start with “Documentation”, but all other changes and additions to documentation are also welcome.

Library improvements


function bit32.countlz(n: number): number

function bit32.countrz(n: number): number

Given a number, returns the number of preceding left or trailing right-hand bits that are 0.

See the RFC for these functions for more information.

Type checking improvements

We have enabled a rewrite of how Luau handles require tracing. This has two main effects: firstly, in strict mode, require statements that Luau can’t resolve will trigger type errors; secondly, Luau now understands the FindFirstAncestor method in require expressions.

Luau now warns when the index to table.move is 0, as this is non-idiomatic and performs poorly. If this behavior is intentional, wrap the index in parentheses to suppress the warning.

Luau now provides additional context in table and class type mismatch errors.

Performance improvements

We have enabled several changes that aim to avoid allocating a new closure object in cases where it’s not necessary to. This is helpful in cases where many closures are being allocated; in our benchmark suite, the two benchmarks that allocate a large number of closures improved by 15% and 5%, respectively.

When checking union types, we now try possibilities whose synthetic names match. This will speed up type checking unions in cases where synthetic names are populated.

We have also enabled an optimization that shares state in a hot path on the type checker. This will improve type checking performance.

The Luau VM now attempts to cache the length of tables’ array portion. This change showed a small performance improvement in benchmarks, and should speed up # expressions.

The Luau type checker now caches a specific category of table unification results. This can improve type checking performance significantly when the same set of types is used frequently.

When Luau is not retaining type graphs, the type checker now discards more of a module’s type surface after type checking it. This improves memory usage significantly.

Bug fixes

We’ve fixed a bug where on ARM systems (mobile), packing negative numbers using unsigned formats in string.pack would produce the wrong result.

We’ve fixed an issue with type aliases that reuse generic type names that caused them to be instantiated incorrectly.

We’ve corrected a subtle bug that could cause free types to leak into a table type when a free table is bound to that table.

We’ve fixed an issue that could cause Luau to report an infinitely recursive type error when the type was not infinitely recursive.

86 Likes

This topic was automatically opened after 10 minutes.

It’s amazing to see that roblox is extending lua to include types I have a few comments

  • Global Type definitions: For example a profile in a profileservice module has a distinct structure it would be great if we could declare global types and add them to a variable and properties of an object for example Profile.Data could be a KeyOf a SaveStructure file.

  • A force strict as default option.

Overall I love the features the dev team has implemented and I look forward to seeing more updates in the future :heart_eyes: :smiling_face_with_three_hearts:.

7 Likes

Great, thanks for all these improvements! Also, would it be good to have the possibility to have a function named import, so we can put all the content of a table in an environment? In other words, something like namespace and import keywords in languages like C++ and python.

Before:

local Content = require(Module)
local CustomService1 = Content.CustomService1
local CustomService2 = Content.CustomService2

CustomService1:Run()
CustomService2:Test()

After:

import(require(Module)) --> import only accepts a table as argument. (it will also automatically put the content of the table as global).
CustomService1:Run()
CustomService2:Test()

I like to have all my stuff defined and I have like 20 custom services so imagine all local things I do.

Is currently possible to achieve this task using getfenv, but it deoptimizes the environment according to the luau documentation. The Studio Script Editor doesn’t even detect stuff inserted into the environment. This is why I am requesting an import function.

image


12 Likes

This is an important one. A script which implements a type cannot share its types with scripts that it depends on, which is a common occurrence with no good workarounds.

4 Likes

this would be genuinely such a useful function to have, and make it much easier to set up modules and services in code. i use the knit framework on top of a bunch of services in my code and the giant mass of requires at the top of my code in some scripts isn’t pretty haha

4 Likes

I agree I use roblox ts and the tooling is absolutely amazing.

4 Likes

Roblox has done so much progress on making Luau so powerful that I can barely distinguish it from lua 5.1. I’m loving how this is going.

4 Likes

All of these stuff are great!

I can add that this month was my birthday

4 Likes

Would love to be able to do variable math assignment on more than 1 value at once, I’m actually surprised this isn’t supported. An example would be:

local Variable1, Variable2 = 1, 2
Variable1, Variable2 += 4, 4          -- Result: 5, 6

Another feature which I think would be helpful which I’m borrowing from JavaScript:

local AVariable = "embed"

local AString = `Here is something to ${AVariable}.` -- "Here is something to embed."
5 Likes

Exactly, in Roblox-ts I’m able to make global type declarations like this and get auto complete even on the client.

declare global {
	interface KnitServices {
		DataService: typeof DataService
	}
}
2 Likes

Why would you need something like this if you can use lua string concatenation

local str = "embed"
local variable = "Here's something to "..str.."."
3 Likes

Mainly ease of reading, while concatenation is good, it often becomes tricky when dealing with many different parses, I find that JavaScripts alternative syntax here with ${Value} is much easier to keep track of things.

JavaScript also has string concatenation, (standard +) but many have found ${} to be preferred.

3 Likes

Multiple compound assignments is confusing, this is intentionally not supported.
Doing two compound assignments will make the code much clearer:

local Variable1 = 1
local Variable2 = 2
Variable1 += 4
Variable2 += 4
2 Likes

Well I have some good news for you:

3 Likes

Even more confusing than it seems at first in the presence of multiple return values. Should this work:

function multiple()
    return 4, 2
end
local a, b = multiple() -- Works
a, b += multiple() -- Does it work ?🤔

So yes, because of a lot of potential confusion we explicitly decided to not handle multiple values at a time with the compound operators.

4 Likes

Aha, I knew I wasn’t the only one that wanted this! Glad to see its in the pipe line.

1 Like