Luau Recap: April 2021

Another busy month in Luau with many performance improvements!

Editor features

Luau implementation now provides an internal API for type-aware autocomplete suggestions.

Roblox Studio will be the first user of this API and we plan for a new beta feature to come soon in addition to existing Luau-powered beta features like Go To Declaration, Type Hovers, and Script Function Filter (you should check those out!)

Performance improvements

Performance is a very important part of Luau implementation and we continue bringing in new performance optimizations:

  • We’ve finished the work on internal vector value type that will be used by Vector3 type in Roblox. Improvements of up to 10x can be seen for primitive operations and some of our heavy Vector3 benchmarks have seen 2-3x improvement. You can read more about this feature here
  • By optimizing the way string buffers are handled internally, we bring improvements to string operations including string.lower , string.upper , string.reverse , string.rep , table.concat and string concatenation operator .. . Biggest improvements can be seen on large strings
  • Improved performance of table.insert and table.remove . Operations in the middle of large arrays can be multiple times faster with this change
  • Improved performance of internal table resize which brings additional 30% speedup for table.insert
  • Improved performance of checks for missing table fields

Generic functions

We had to temporarily disable generic function definitions last month after finding critical issues in the implementation.

While they are still not available, we are making steady progress on fixing those issues and making additional typechecking improvements to bring them back in.

Debugger improvements

Debugging is now supported for parallel Luau Actors in Roblox Studio.

Read more about the feature over here and try it out yourself.

Behavior changes

Backwards compatibility is important for Luau, but sometimes a change is required to fix corner cases in the language / libraries or to improve performance. Even still, we try to keep impact of these changes to a minimum:

  • __eq tag method will always get called for table comparisons even when a table is compared to itself

Coming soon…

  • Better type refinements for statements under a condition using a new constraint resolver. Luau will now understand complex conditions combining and / not and type guards with more improvements to come!
100 Likes

This topic was automatically opened after 15 minutes.

April 2021 actually added a lot of stuff!

I’m not sure what this means, but I saw a topic about it

Most of my scripts are using large scripts, thank you for this update!

Finally, a good reason to use table.insert and remove! I knew using it was good

Does this mean that table.create is slower, equal, or faster than normal tables now?

I’m not sure what these mean? Can someone help me?

This may break some games, but only a few and only some stuff in them.

Do these include performance improvements :thinking:

3 Likes

Sounds good, can’t wait for more updates!

3 Likes

These improvements are needed, and I hope there will be more of these. My game performance had improved by a lot due to the fact it uses lots of stuff that was listed here. Good Job.


Will we see more of performance improvements anytime soon?

2 Likes

It’s a function defined for polymorphism.

the use of a single symbol to represent multiple different types

Basically, you can give a type argument for a function. It works just as generic types:

type genericType<typeparam1, typeparam2> = { [typeparam1] : typeparam2 }
-- Type becomes { [string] : number}
local example: genericType<string, number> = {stringIndex = 1}

I haven’t messed with generic functions in Luau yet, but here’s a C++ example:

template <typename T> // Can be literally anything, T is just a convention
void example(T someParam, T anotherParam) {
    std::cout << someParam << anotherParam << std::endl;
}

int main() {
    // Becomes void example(string someParam, string anotherParam)
    example<std::string>("Hello, ", "World!"); --> Hello, World!
    // Becomes void example(int someParam, int anotherParam)
    example<int>(1, 2); --> 12
}

Edit: Started looking into Luau generic functions, the equivalent of the above C++ code would be: (keep in mind generic functions don’t work as of now)

local function example<T>(someParam: T, anotherParam: T)
    print(tostring(someParam), tostring(anotherParam))
end

-- Becomes example(someParam: string, anotherParam: string)
example<string>("Hello, ", "World!") --> "Hello, World!"
-- Becomes example(someParam: number, anotherParam: number)
example<number>(1, 2) --> 12

Now that I have the chance, I’ll leave words of encouragement for the team:
Very good job! :sunglasses: :+1:

2 Likes

LuaU has been making great strides in the realm of its type strict option. The optimizations as I often say help expand the realm of posibilities for what is possible with roblox.

Nice to see that changes are being made to debugging, this will make it easier for newer scripters and more experienced scripters alike to have easier workflow.

One thing that might be nice to see is prioritization of certain complex logic clauses being moved up, but this is a mimor thing that has minimal impacts.

Keep it up!

Ooh! Now I understand what they are!

I never used them, and never used that weird luau thingy with stuff like

local x:y = y

so yeah

1 Like

Does this mean that table.create is slower, equal, or faster than normal tables now?

TL;DR: nothing has changed when it comes to using table.create vs. {}

Considering that an all-in-one memory allocation is pretty much always going to be faster than allocating it procedurally (e.g standard table appendation) table.create is not going to be any more or any less practical as of this update

Also just a small gripe with your post:

table.create is slower, equal, or faster than normal tables now?

table.create is a normal table, it’s just preallocated, but i know what you meant

4 Likes

I have some general questions and about some stuff in Luau.

For starters, when can we expect to see generic functions actually released? I’ve been looking forward to using them for some code that needs them but they just keep getting pushed back.

As for problems, I’ve been running into an annoying one where the type of assert is defined incorrectly. It should be <T>(T, string?) -> T but is actually just <T>(T) -> T. I also can’t get methods of Roblox built-ins to give me any sort of type other than any i.e workspace:FindFirstChild("Baseplate") has a return type of any where it should be Instance?

Sorry about being that one guy whos always complaining something about Luau related. I’m genuinely impressed with some of the optimizations that are being made. (Especially the vector3 native type stuff thats nuts.) Keep up the work and I hope to see things get even better.

1 Like

Wow, wow, wow. The performance improvements just keep coming! I’m amazed that this much performance improvement is still possible after all the previous ones as well! Keep up the great work!

This makes ambitious projects so much more feasible.

As for problems, I’ve been running into an annoying one where the type of assert is defined incorrectly. It should be <T>(T, string?) -> T but is actually just <T>(T) -> T .

We hope to release a fix for this very soon.

I also can’t get methods of Roblox built-ins to give me any sort of type other than any i.e workspace:FindFirstChild("Baseplate") has a return type of any where it should be Instance?

We are working to improve type information from workspace elements and Roblox APIs and workspace:FindFirstChild return type will register as Instance.
Hopefully we can get this improvement this month!

1 Like

Does this mean that in the next update we will be getting the ability to not have our code spazz out saying stuff like Size is not a valid member of Instance? after doing something like

local example = workspace:FindFirstChild("Example")
example.Size = Vector3.new(exampleXValue, exampleYValue, exampleZValue)


Also, a bit unrelated to the quote, when will we be getting the ability to define function types for variables? Or are we even going to be getting this ability?

1 Like

You can already use type assertation operator (::) to get around the valid member errors:

local example = workspace:FindFirstChild("Example") :: BasePart --workspace:FindFirstChild("Example") result will be treated as BasePart so example variable will act like a BasePart.
example.Size = Vector3.new(exampleXValue, exampleYValue, exampleZValue)
1 Like

Does this mean that in the next update we will be getting the ability to not have our code spazz out saying stuff like Size is not a valid member of Instance?

An improvement for FindFirstChild with exact workspace types wasn’t planned for this month, but I’ve created a ticket to track this issue.

3 Likes

Has the type of things like FindFirstChild been slowly regressing or am I going insane? I took a screenshot a couple weeks ago where it was (Instance, string, boolean?) -> Instance and I swear the return used to be nullable but now its just any. If it has been regressing, what’s the reason for it?

You’ve made some awesome and very complex performance improvements, but as an end user of the language I feel like there is too much focus on performance and not enough on usability.

For example, typing OOP code (the dominant paradigm among users on roblox) is still… problematic. From the Luau guide:

A downside with this pattern is that it does not automatically create a type binding for an instance of that class, so one has to write type Account = typeof(Account.new("", 0)).

Having to call your constructor to define the type works for simple cases, but what my Account constructor required a reference to a Bank class? Now I have to do Account.new(“john”, 500, Bank.new()) and also pass the correct type of arguments to the Bank object. You can see how this could easily get out of hand.

A second problem is the way types are exported. It’s extremely common to have 1 class per ModuleScript, like Java. When a type is exported you access it like you would any other exported variable. Now your code looks like this:

-- file: Bank.lua
local Account = require(path.to.Account)

local firstAccount: Account.Account = Account.new("John", 500)

Now Account means 2 different things, depending on the context. Pretty confusing, and also strange syntax in most cases.

It also seems like adding that type annotation doesn’t make the linter aware of the types of the class properties (in fact, it crashed studio when I tried doing firstAccount.balance = "a")

In my use case, Luau can’t be used for anything other than simple type hints for primitive types. I’ve considered switching to C style syntax where tables hold just data, and functions are completely separate. I’m excited for the day when we’ll be able to get autocomplete hints when accessing fields on tables and oop style objects :slight_smile:

3 Likes

We have more people working on type checking improvements than on performance, it just takes time :slight_smile:

We also do plan to improve the OOP situation, right now we’re focusing on fully correct and robust coverage of the existing language though.

Would you mind sharing an example that reproduces this problem?

1 Like

Is there a list of all the primitives? (I mean the new ones you’re adding… not booleans and integers lol)