Luau Recap: February 2021

I love generic methods in C#
Can’t wait to see them here as well

Thanks a lot for the updates

9 Likes

Yes, those annotations are part of the Language spec, not just a temporary beta thing: Type checking - Luau

As for how type-system driven optimization will interact with mixed code, that’s a hard problem. At the end of the day you’ll have to profile and see when that eventually becomes available, however, using type assertions and other techniques it’s a safe bet that you will always be able to kick the available optimizations into working for whatever specific pieces of code you need to make fast even if the rest of your code doesn’t have annotations.

6 Likes

There’s also this bug where after having studio open for a while, studio will stop auto completing global variables:


Just noticed in the video it says luau ran into an error and will not type check until studio is restarted.

6 Likes

This is absolutely true and annoying. How do you disable LuaU?

3 Likes

You can’t disable Luau, it’s literally the language you use to code in Roblox. You’re thinking of the Luau Type Checker, in which case, you can’t since it’s built in.

5 Likes

Luau is the variant of Lua that roblox runs on. It is basically what people call “Roblox Lua”. It has the stuff roblox has in it. Pure lua is very similarly. Luau is just a fork of Lua with better features and such to work on Roblox.

3 Likes

Did you read what I even said… Compound operators for the logical AND/OR operators

2 Likes

I’m sure this has been asked in the past but are there any plans for manual garbage collection? Like how one would create then free an array in C

int *array = malloc(5 * sizeof(int));

free(array);

maybe something in luau like

local array = table.create(5);

delete(array);

I know in regular lua you would do this a-la

local array =  { 1, 2, 3, 4, 5 };

array = nil;

collectgarbage('collect');

I really only ask because there’s no (easily-findable) documentation on how Luau does it’s gc other than… this

5 Likes

What are your uses for having a way to manually trigger garbage collection?

It feels very footgun-y. You shouldn’t ever have to worry about what the garbage collector is doing behind the scenes because if you’re designing code around it, you’re doing it wrong.

All you have to really know is that once an object (table, userdata) falls out of scope, it will cease to exist in memory at some point in the near future.

1 Like

all cool, however, is there some method of defining functions inside of tables when assigning type to it? For example this

export type external = {
    Test: (boolean) -> (),
}

local External: external = {}

function External.Test(Bool)
    print(Bool)
end

return External

Will give a warning W000: (7,1) Table type 'External' not compatible with type 'external', missing field 'Test', i thought of doing this

local External: external = {
	Test = function(Bool)
		print(Bool)
	end,
}

return External

However it’s ugly, possible way could be changing the function type to Test: (boolean) -> ()? but that will cause other warnings like W000: (13,1) Cannot call non-function ((boolean) -> ()) | nil when calling it for obvious reasons. Same thing happens with the properties.

For module scripts i can fix it by instead of assigning the type when creating the table, use the type assertion operator

return External :: external
2 Likes

Lack of transparency is my largest problem here. My use-case would be to immediately dispose of tables that hold a lot of data within them - if all I know is that the gc would collect them “at some point” then I’d feel more comfortable collecting the garbage on my own when its time to dispose of these objects. I understand how one could think this is a footgun but until Roblox tells us what’s happening “behind the scenes” I feel like we should at least get solid reasoning as to why we can’t do it ourselves other than new scripters might blow their ro-toes off. Considering script injection exploits release within hours after every client build release, I don’t see how it would open the client up to anything worse other than an overflow/crash. Then again my knowledge of cpp is amateur at best - this is where I circle back to transparency and lack of documentation on everything aforementioned

3 Likes

An overflow (as in your example) is already a critical security vulnerability. The most likely issues would come from double free and use after free on pointers. Memory issues like these make up about 70% of all attack vectors (according to both Google and Microsoft in independent studies).

Also, it simply isn’t necessary to do it manually. If you don’t have enough memory, GC will run.

3 Likes

Fair enough. But again, this provides no insight into how Luau gc is actually performed. However this is where I’ll stop bringing it up, for now at least

2 Likes

We don’t have any plans to introduce manual memory management - it can be error prone and can violate strict sandboxing guarantees that our implementation provides.

When we finish the garbage collection implementation we will likely write a separate page explaining a bit how garbage collection works, as some of these details are interesting and/or help explain the behavior of GC, and some help understand how to write efficient programs.

5 Likes

Thanks for the response. I’m looking forward to reading the documentation

1 Like

I’ve run into a bit of a pickle with luau syntax

So I have this variable called “playingTrack” which is typed as AnimationTrack?

So when I call the following:

	playingTrack = animator:LoadAnimation(animation)
	playingTrack:Play(
		animation:GetAttribute('EmoteFadeTime'),
		nil,
		animation:GetAttribute('EmoteSpeed')
	)

I get this warning for the second line:
image
In this context, I know for sure that the animationTrack exists. So the logical solution is to add a type annotation to overcome the limitations of the type system:

	playingTrack :: AnimationTrack:Play(
		animation:GetAttribute('EmoteFadeTime'),
		nil,
		animation:GetAttribute('EmoteSpeed')
	)

Looks like this will be a syntax error, so I’ll just wrap playingTrack :: AnimationTrack in parentheses, right?

	(playingTrack :: AnimationTrack):Play(
		animation:GetAttribute('EmoteFadeTime'),
		nil,
		animation:GetAttribute('EmoteSpeed')
	)

Nope, that just leads to ambiguous syntax

I guess I’ll just add a semicolon at the beginning of the line.

	;(playingTrack :: AnimationTrack):Play(
		animation:GetAttribute('EmoteFadeTime'),
		nil,
		animation:GetAttribute('EmoteSpeed')
	)

Nope, that adds this warning

No matter how much indentation I give that line, it still says my statement spans multiple lines.

Not sure what to do here.

The solution (and generally the way you should be using it) is to use the as on the variable assignment rather than in the middle of an expression.

local playingTrack: AnimationTrack = nil

playingTrack = animator:LoadAnimation(animation) :: AnimationTrack
playingTrack:Play() --> works
  1. Related to @Anaminus’s report:
local x: {number|number} = {1, 2, 3}
local y = x[1] - x[2] -- Type 'number|number` could not be converted into 'number'
  1. Vector2:Cross returns a number but is incorrectly typed to return a Vector2

  2. Positional array types like typescript would be a cool feature (probably pending the fix of the first bug though):

type Pair<T,U> = {T, U}

My playingTrack was typed as AnimationTrack?, not AnimationTrack

local playingTrack: AnimationTrack? = nil

-- ... closure involving playingTrack

playingTrack = animator:LoadAnimation(animation) :: AnimationTrack
playingTrack:Play() --> does not work

image

Oh, I see the problem, you want Type? = Type to effectively assert temporary narrowing of the variable, that would make sense.