Luau is our new language that you can read more about at https://roblox.github.io/luau ; we’ve been so busy working on the current projects that we didn’t do an update in September, so let’s look at changes that happened since August!
Many people work on these improvements, with the team slowly growing - thanks @Apakovtac, @EthicalRobot, @fun_enthusiast, @machinamentum, @mrow_pizza and @zeuxcg!
Types are very close
We’ve been in beta for a while now, but we’re steadily marching towards getting the first release of the type checker, what we call “types v0”, out of the door. It turns out that we’ve substantially underestimated the effort required to make the type system robust, strike the balance between “correct” and “usable” and give quality diagnostics in the event we do find issues with your code
Because of this, we’re changing the original plans for the release a bit. We’re actively working on a host of changes that we consider to be part of the “v0” effort, and when they are all finished - which should happen next month, fingers crossed - we’re going to be out of beta!
However, by default, on scripts with no annotations, we won’t actually activate type checking. You would have to opt into the type checking by using --!nonstrict
or --!strict
, at the top of each script. We are also going to open the second beta, “All scripts use non-strict mode by default” or something along these lines.
This is important because we found that our non-strict mode still needs some more work to be more tolerant to some code that occurs commonly in Roblox and is correct, but doesn’t type-check. We’re going to evaluate what changes specifically are required to make this happen, but we didn’t want the extra risk of a flood of reports about issues reported in existing code to shift the release date in an unpredictable fashion.
To that end, we’ve been working on Lots and Lots and Lots and Lots and Lots of changes to finish the first stage. Some of these changes are already live and some are rolling out; the amount of changes is so large that I can’t possibly list the up-to-date status on each one as these recaps are synthesized by the human who is writing this on a Friday night, so here’s just a raw list of changes that may or may not have been enabled:
- Strict mode is now picky about passing extra arguments to functions, even though they are discarded silently at runtime, as this can hide bugs
- The error message about using a
:
vs.
during type checking is now much more precise - Recursive type annotations shouldn’t crash the type checker now, and we limit the recursion and iteration depth during type checking in a few cases in general in an effort to make sure type checker always returns in finite time
- Binary relational operators (
<
et al) are now stricter about the argument types and infer the argument types better - Function argument and return types are now correctly contra- and co-variant; if this looks like gibberish to you, trust me - it’s for the best!
- Fixed a few problems with indexing unions of tables with matching key types
- Fixed issues with tracing types across modules (via
require
) in non-strict mode - Error messages for long table types are now trimmed to make the output look nicer
- Improve the interaction between table types of unknown shape (
{ [string]: X }
) and table types of known shape. - Fix some issues with type checking table assignments
- Fix some issues with variance of table fields
- Improve the legibility of type errors during function calls - errors now point at specific arguments that are incorrect, and mismatch in argument count should clearly highlight the problem
- Fix types for many builtins including
ipairs
,table.create
,Color3.fromHSV
, and a few others - Fix missing callbacks for some instance types like OnInvoke for bindables (I think this one is currently disabled while we’re fixing a semi-related bug, but should be enabled soon!)
- Rework the rules under which globals are okay to use in non-strict mode to mostly permit valid scripts to type-check; strict mode will continue to frown upon the use of global variables
- Fix a problem with the beta where two scripts with identical names would share the set of errors/warnings, resulting in confusing error highlights for code that doesn’t exist
- Improve the legibility of type errors when indexing a table without a given key
- Improve the parsing error when trying to return a tuple;
function f(): string, number
is invalid since the type list should be parenthesized because of how our type grammar is currently structured - Type checker now clearly reports cases where it finds a cyclic dependency between two modules
- Type errors should now be correctly sorted in the Script Analysis widget
- Error messages on mismatches between numbers of values in return statements should now be cleaner, as well as the associated type mismatch errors
- Improve error messages for comparison operators
- Flag attempts to require a non-module script during type checking
- Fix some cases where a
type
/typeof
guard could be misled into inferring a non-sensible type - Increase the strictness of return type checks in strict mode - functions now must conform to the specified type signature, whereas before we’d allow a function to return no values even in strict mode
- Improve the duplicate definition errors to specify the line of the first definition
- Increase the strictness of binary operators in strict mode to enforce the presence of the given operator as a built-in or as part of the metatable, to make sure that strict mode doesn’t infer types when it can’t guarantee correctness
- Improve the type errors for cyclic types to make them more readable
- Make type checker more friendly by rewording a lot of error messages
- Fix a few crashes in the type checker (although a couple more remain - working on them!)
- … I think that’s it?
- …edit ah, of course I forgot one thing - different enums that are part of the Roblox API now have distinct types and you can refer to the types by name e.g.
Enum.Material
; this should go live next week though.
If you want to pretend that you’ve read and understood the entire list above, just know that we’ve worked on making sure strict mode is more reliably reporting type errors and doesn’t infer types incorrectly, on making sure non-strict mode is more forgiving for code that is probably valid, and on making the type errors more specific, easier to understand, and correct.
Type syntax changes
There’s only two small changes here this time around - the type syntax is now completely stable at this point, and any existing type annotation will continue parsing indefinitely. We of course reserve the right to add new syntax that’s backwards compatible
On that note, one of the small changes is that we’ve finally removed support for fat arrows (=>
); we’ve previously announced that this would happen and that thin arrows (->
) are the future, and had warnings issued on the legacy syntax for a while. Now it’s gone.
On a positive note, we’ve added a shorter syntax for array-like table types. Whereas before you had to use a longer { [number]: string }
syntax to declare an array-like table that holds strings, or had to define an Array
type in every. single. module. you. ever. write. ever., now you can simply say {string}
! This syntax is clean, in line with the value syntax for Lua table literals, and also was chosen by other research projects to add type annotations to Lua.
(if you’re a monster that uses mixed tables, you’ll have to continue using the longer syntax e.g. { [number]: string, n: number }
)
Parallel Luau is in progress!
We’re actively working on our first beta release of parallelism support. We plan to ship something by the end of the year in Studio as a beta - we want to keep iterating on some possibly-breaking changes, so this will be a beta-only release, but we’re excited to share our plans with you.
The release will also be a small part of the full vision - the full technical specification for this is 26 pages (this was hard to write…), and we aren’t at the point where we implemented the entire thing - not even close Our plan is to gradually expand what you can do in parallel over the course of the next year (in addition to releasing this outside of beta of course!), while maintaining safe sandboxing (if you manage to crash the engine by running code in parallel, that’s always a bug on our end).
For people who are daring and adventurous, I’ll leave a few hints and you can try to explore this on your own: ParallelLua
, Actor
, Heartbeat:connectParallel
, TaskLibrary
, task.synchronize
, task.desynchronize
. Connecting the pieces is up to you until we actually release a beta
Library changes
There’s only a few small tweaks here this time around on the functionality front:
-
utf8.charpattern
is now exactly equal to the version from Lua 5.3; this is now possible because we support\0
in patterns, and was suggested by a user on devforum. We do listen! -
string.pack
now errors out early when the format specifier is Way Too Large. This was reported on dev forum and subsequently fixed. Note that trying to generate a Moderately Large String (like, 100 MB instead of 100 GB) will still succeed but may take longer than we’d like - we have a plan to accelerate operations on large strings substantially in the coming months.
Performance improvements
We were super focused on other things so this is very short this time around. We have a lot of ideas here but they are waiting for us to finish some other large projects!
- Method calls on strings via
:
are now ~10% faster than before. We still recommend using fully-qualified calls from string library such asstring.foo(str)
, but extra performance never hurts! - Speaking of string methods,
string.sub
is now ~20% faster than before with the help of voodoo magic.
Miscellaneous fixes
There were a few small fixes that didn’t land into any specific category that I wanted to highlight:
- In some rare cases, debug information on conditions inside loops have been fixed to stop debugger from incorrectly suggesting that the current line is inside a branch that wasn’t taken. As usual, if you ever see debugger misbehaving, please file bugs on this!
- Code following
assert(false)
is now treated as an unreachable destination from the linting and type checking point of view, similarly toerror
calls. - Linting support for various format strings has been greatly improved based on fantastic feedback from @Halalaluyafail3 (thanks!).
Ok, phew, that’s what I get for skipping a month again. Please don’t hesitate to report bugs or suggestions, here or via separate posts. Due to our usual end-of-year code freeze there’s going to be one more recap at the end of the year where we will look back at 2020 and take a small peek into the future.