Luau Recap: October 2020

The new shorthand array syntax is very much appreciated and will definitely make a lot of code more succint. Thank you :slight_smile:

On a side note, can we expect enum support for this initial release? I realise it’s probably a bit late to ask for this now but it’s one of the few things I can’t annotate precisely in my code, settling with just specifying EnumItem as the type and dealing with any wrong enums passed as they come by (Enum.InputType and Enum.UserInputType are some I catch myself swapping often, for instance).
EDIT: Oh whoops, I missed that edit there. Glad to know that enums were given some love and that this is no longer a problem. Thanks again :smile:

Other than that, the lack of type assertions mentioned by others also complicates code in situations where I know something is not nil but the type checker does not, forcing me to resort to redeclaring variables to cast types to any and back (or just using any everywhere altogether). This comes up more often than I expected and is the major issue blocking me from using typing more proactively, so anything on this front would be immensely helpful.

Thank you for the amazing work on Luau so far. It has saved me from myself many times already.

6 Likes

Luau does not change anything about lua’s syntax or its behavior, its just an extension of lua with better performance, and additional features (like type checking).

As for the recap, I am very hyped! I’m once again excited to optimize my compression algorithm, unfortunately of which I’ve hardly had time to work on (yay school, Covid, and 2020)

The hints make me very curious. Here is what I guess that they will do with my limited understanding of how the heck multithreading tends to work:
ParallelLua - For this, I have no idea what this could be… Other than a library. Based on the wording, I think this will have constructors or helpers, e.g. ParallelLua.new or ParallelLua.spawn, but, I’m not sure what this may create. Perhaps the Actor could be related to its return type but I more so suspect it will aid in the creation of several types.
Actor - I think this could potentially be a data type, or possibly a class of Instance which would facilitate certain parallel behaviors between scripts. Basically, scripts could signal each other through the Actor and unlike events everything would be happening on different CPU threads. It’ll be interesting though to see what all this may have.
Heartbeat:connectParallel - This very clearly refers to the Heartbeat event of RunService, and, I think very clearly indicates that all events could be connected so that their callbacks are run parralel - Or perhaps instead we would pass tasks?
TaskLibrary - This very clearly will be used in the creation of tasks, of which I assume the type or class name will be Task?
(These two I have the least confidence on)
task.synchronize - This could potentially be used to synchronize two or more tasks together so that you could guarantee one task will process in unison with another, perhaps to stop race conditions so if one task is running slow the other one won’t race ahead through your code.
task.desynchronize - This could do the opposite, allowing the task to process whenever it felt like even if another task is processing faster, so if one is running slower it would fall behind.

10 Likes

Very happy to see this post as a whole, I said in the 2021 RDC speakers survey that I wanted to hear more Luau progress, looks like Christmas came early! I also explicitly mentioned I wanted to hear more about parallel Lua, so it’s a cool coincidence that this got mentioned now…

Being able to run parallel processes on heartbeat should be a fairly simple and elegant way to enable multithreading in a very practical/easy to implement way. i.e if you have some AI that runs for each zombie in a game, and that AI script runs off of heartbeat, you could easily split that work to run on separate threads with a “Heartbeat:connectParallel” (I assume that’s what that means).

I assume there is a “task” object which is constructed with a function as a parameter, and then you can “synchronize” two tasks together where these two linked tasks basically behave as one yield in the lua thread, and the yield stops once the execution of both tasks finishes; luau would decide whether or not to place those tasks on multiple hardware threads based on availability?

2 Likes

Awesome! I can’t wait until we get to V1 of typed luau.

It looks like OOP is also (kind of) reasonable to use now when relying mostly on type inference and not-so-sugary syntax. However, the type checker seems to not like __index calls outside of the class code. This seems like a regression, as before I was able to call functions defined in a table’s __index metamethod:

game.ReplicatedStorage.Person (no script analysis errors):

--!strict

local Person = {}
Person.__index = {}

function Person.new(_name: string, _dob: number)
	local self = {}
	setmetatable(self, Person)
	
	self.name = _name
	self.dateOfBirth = _dob
	
	return self
end

-- Exported as Person.Class for convenience
export type Class = typeof(Person)
-- Exported as Person.Object for convenience
export type Object = typeof((function() local _name: string, _dob: number return Person.new(_name, _dob) end)())

function Person.__index.PrintName(self: Object): ()
	print(self.name)
end

function Person.__index.GetAge(self: Object): number
	return math.floor((os.time() - self.dateOfBirth) / 365.25 / 24 / 60 / 60)
end

return Person

game.ServerScriptService.Script (script analysis error on the last line):

--!strict

local Person = require(game.ReplicatedStorage.Person)

local bob = Person.new("Bob", 0)

print(bob:GetAge())

Output when running the game (works as expected):

image
Yes, it’s really been 50 years since the Epoch. Wow.

I’m also still waiting for the day we’ll be able to import exported types from a module without requiring that module, in order to avoid some require deadlocks.

Overall though, I’m glad to see this project becoming more and more usable in production code!

4 Likes

After countless hours working on a project from vscode and importing to Roblox, I’ve come across one of the oddest bugs. Every time I edited code in a specific script it crashed studio. Now with it being over 700 lines I immediately went straight to Netflix. Hours later I built up the courage to pinpoint the problem and ended up finding something.
what.rbxm (824 Bytes)
image
By removing the comment from that line it will immediately crash your studio and it seems to happen only when type checking is on. Assuming it has some kinda cyclic problem, but I just can’t seem to figure out why.

Didn’t mean to reply directly to DataBrain although I do love some of his ideas

I’m also still waiting for the day we’ll be able to import exported types from a module without requiring that module, in order to avoid some require deadlocks.

4 Likes

EDIT: Haha nope I was wrong, go look at the actual post about it here

Old post here

While I am by no means an expert on multithreading, I believe I’ve gotten Parallel Lua working (at least a little bit). Testing was done on sitetest3, but this stuff exists on sitetest1 so it’s probably fine to use that. For anyone curious, these are my observations:

  • ParalellLua and TaskLibrary are FFlags that you should turn on to mess with Parallel Lua
  • Actor is a new Instance that acts as a container for parallel code (@zeuxcg is there a reason it inherits from Model and isn’t its own thing like Folder?)
    • Scripts which use ConnectParallel must be parented under an Actor to run
  • ConnectParallel is a new method on RBXScriptConnection
    • this method runs the passed function in parallel but is otherwise normal
  • task is injected into the environment of scripts that are parented under Actor instances
    • it contains synchronize, which locks the DataModel to the calling coroutine, and desynchronize which unlocks it
    • if synchronize isn’t called in a coroutine, it will throw an error when trying to access the DataModel

I ran this code to test it:

local counter = Instance.new("IntValue")
counter.Name = "Counter"
counter.Parent = workspace

for i = 1, 10 do
    local connection
    connection = game:GetService("RunService").Heartbeat:ConnectParallel(function()
        task.synchronize()
        counter.Value += 1
        task.desynchronize()
        connection:Disconnect()
    end)
end

On my not-very-good CPU, that set counter.Value to 65 instead of 10 (which is what you might expect looking at the code).

My conclusion: multithreading is hard but the results are promising, and I’m looking forward to actual documentation instead of just figuring it out as I go along.

31 Likes

The biggest bottleneck in Luau for me at least would be the lack of as keyword. Let’s say I have a variable with a type sometype | nil. At some point in my code the type checker will complain about no key somekey found in table sometype | nil although I’m sure it’s not nil but the type checker isn’t.

Other programming languages have type assertion, casting etc, why doesn’t Luau?

4 Likes

Will we be able to update instance properties in parallel? Or will the properties have a mutex lock on them? I would really like for the code running in the parallel threads to not lock resources (including Lua tables) while reading them or writing to them. This part is very crucial to be able to benefit from parallel computing.

Any plans for compute shaders?

6 Likes

Nooo! So does this mean that all the roblox scripting tutorials on youtube won’t work? Now I’m really not gonna know how to code… :frowning:

3 Likes

I read the entire list. I won’t pretend I fully understood all of it (yet) however that gives me goals to move towards. Just wanted to note that I did indeed read it and I’m sure there are many that will, even if most won’t.

I’m actually very glad there will be a type checker but I would think that would be a very extensive project.

I don’t know if you have seen this one on github for Lua but it’s a good idea of project depth you’re facing:

There’s so many extra functions specific to Roblox too and all the ways those apply.

Good luck!

1 Like

Luau is fully released, the type checker is still in beta, and when it is released I bet it will still have a toggle option.

2 Likes

This question gets asked often, and the answer is always the same:

You can use as many or as few of the new features as you’d like. Some of them may make you more productive in the long run, but 100% of the code you know how to write today will continue to work, so if you just want to use what you know today - you don’t need to learn anything else.

6 Likes

bruh → makes no sense, because “=” means equals and => <= makes more sense cause it means equals to or larger etc… ‘-’ more like a minus

Awesome! Cant wait to learn more about luau :heart_eyes:

1 thing tho, can luau be used in a lua script, like a mix?

1 Like

Luau is a fork of Lua with extra features added.

2 Likes

I’m assuming its to prevent typos and confusion as >= and <= are used in comparisons, and presumably to make typed luau more in line with other typed languages.

6 Likes

I tried making a random prime finder script I wrote in 5 minutes parallel. Well, I guess it works as the resource monitor shows 6 threads more than without running it parallel, but it is definitely in its early stages.

This might not be related, but it seems my average CPU usage in studio can never go over 25% , which makes testing performance benefits really hard.

4 Likes

It is an arrow. How do you write arrows in normal text?

1 Like

Any changes on the handling of nullable types? My main concern is whether or not values that are specifically tested to be non-nil will be treated as such in the type checker.

Example:

function SomeFunction(someArg: string?)
    if (someArg ~= nil) then
        SomeOtherFunction(someArg)
        -- This will complain about mismatch between string and (string | nil)
        -- despite the nil check just above.

        -- Will the type checker ever be conscious about checks like this?
    end
end

function SomeOtherFunction(someArg: string)
    -- Do something with the non-nullable string here.
end

Also, what about how modules load types? I always thought of a require where the target module exports types to be akin to an include or using statement, but from what I can tell on the Luau website, referencing those types is only possible via:

local Module = require(...)
local Var: Module.ExportedType = ...

Are there any plans to lift the requirement of prepending Module. onto the type expression?

3 Likes

Right now you can use if someArg then, which will refine the type of someArg within the if block to remove the nil variant. We aren’t doing this for comparisons or for and/or chains, which is planned but not for v0 (so will likely only happen early next year).

No changes in this regard yet; we will need to dive into some use cases for this to make sure having a separate way to import types is valuable here.

Not at the moment, similarly how require always puts the results into the resulting variable’s “scope” (short of using getfenv).

5 Likes