Luau Recap: March 2023

Another great update from the team, thanks! :heart:

3 Likes

Awesome! I can’t wait until Roblox has capability to catch up to other leading game-engines.

3 Likes

I would say having unpack as the DeFacto destructuror would be slightly harder for newcomers to understand. CFrame:GetComponents() is straightforward syntax that explains exactly that it’s doing: returning a tuple of each component.

unpack(CFrame) is undoubtfully more confusing than the above. What exactly are we unpacking from CFrame, its Position or Rotation? unpack(Color3) would be even worse, because a Color3 can be RGB, HSV or Hex. Returning just the R, G, B of a Color3 would leave the others in the dust.


:: any, any isn’t valid syntax in a Type Ascryption.

3 Likes

What about ...type?

2 Likes

Oh yeah just found one oversight that got ghosted when I submitted it as a bug.

I honestly hate how I have to do this workaround because apparently Instance:FindFirstChild() return type Instance instead of Instance? even though the real return type is Instance?. Because of that, I have to do this workaround that somehow just works.
RobloxStudioBeta_A1G3Z
technically (varThatisInstanceType :: Instance?) :: Folder? works too

Attempting to cast directly causes this error because “unrelated types”, which is just frustrating me a lot.
RobloxStudioBeta_lLQ20

4 Likes

native cframes when??

also will we ever see the type of performance demo’d in one of zeuxcg’s hack weeks where he got luau performing comparable to luajit?

3 Likes

You can actually sorta

local function findFirstChild(a: Instance, b: string)
    return a:FindFirstChild(b)
end :: typeof(workspace.FindFirstChild)
4 Likes

I’m assuming this was written in March but published on April.

Luau is looking better now. I’ve seen several performance improvements, but none of them really affected me.

And when are we going to get this?

local constructors = {}
local objects = {}
constructors.constructor = require(modulePath)
objects.myObject = constructors.constructor.new("myValue", 1, 2, 3)
--objects.myObject has no intelliSense :(
5 Likes

Please elaborate.

I believe unpack(CFrame) is no more confusing than CFrame:GetComponents(). The use of unpack would imply that you intend to retrieve everything a value is composed of, in this case, a CFrame’s components.

I’m going to go out on a limb and say you lack the fundamental understanding of what a Color3 even is. Although a Color3 can be constructed with an RGB, HSV, or Hex, it will always be a Color3. A Color3 will always be [stored and read] in the format of RGB, the range being 0-1 instead of 0-255.

The funny thing is tostring does that already *0-1 range, so rather than “leaving the others in the dust”, it would instead reinforce the convention already put in place.

2 Likes

If you mean the same treatment as Vector3, then it’s not planned.

Probably.

4 Likes

i don’t see why there should be a difference
image
vs
image

5 Likes

The improved type refinements sound particularly useful, especially the ability to preserve refinements after a conditional block and enforce ML-style exhaustive analysis. The deprecation of table.getn, the possible replacement of foreach/foreachi and the autocomplete improvements also seem like positive changes. And it’s always good to see runtime improvements, particularly the optimizations made to table.sort and math functions.

2 Likes

I’ve been having a lot of fun trying strict mode, but here are my remaining gripes with it:

--!strict

-- Globals seem to lack type annotations despite intellisense working fine?
-- Not sure when this started or why. First noticed this today, but might've been blind to it
local workspace = workspace::Workspace

-- continue does not refine type like return does
for _,v in ipairs(workspace:GetChildren()) do
	if not v:IsA("BasePart") then continue end
	print(v.Position) -- Key 'Position' not found in class 'Instance'
end

-- For comparison, return refining the type
local function fn(v:Instance)
	if not v:IsA("BasePart") then return end
	print(v.Position) -- OK, but without the type annotation, fn(v) below says the same as v.Position above - that is to say, the refinement does not propagate backward
end
for _,v in ipairs(workspace:GetChildren()) do
	fn(v)
end

-- string.gsub (still?) does not accept `(string) -> string?`
print(string.gsub("testing", ".", function(x)
	if math.random() < 0.5 then
		return string.char(math.clamp(string.byte(x) + math.random(-2, 2), 0, 255))
	end
	-- Valid and convenient, but not recognized by the type checker
	return nil
	-- Alternative: return x
end))
3 Likes

The type of the workspace global is different than type interface’s Workspace type due to former has datamodel hierarchy been applied on it, on top of latter, which only includes the properties, methods, events and maybe callbacks (if there are any). You can see this for yourself by using typeof:

local workspace: typeof(workspace) = workspace
print(workspace.Script) --Perfectly fine

local workspace: Workspace = workspace
print(workspace.Script) --Type Error: (5,7) Key 'Script' not found in class 'Workspace'
2 Likes

Oh, I should’ve brought more examples. I was referring to services coming from GetService having type any because game is any. I swear I remember services having their correct types.

Maybe it never was that way:

local foo = game -- When hovered over: any, not DataModel or Instance

local Selection = game:GetService("Selection") -- any
-- Because game is any, GetService returns any and Get() returns any

local game = game::DataModel
local Selection = game:GetService("Selection") -- Instance
-- Cue large amounts of type warnings because I am doing Instance:Get()
-- This usually doesn't happen, therefore either game has been any all along
-- or GetService used to return the correct type, but no longer does so

local Selection = game:GetService("Selection")::Selection -- Selection
-- Fully expected behavior
-- Selection:Get() returns {Instance}

So that means I should be casting all of my services to the correct type, and should’ve been all along.

I also have to cast plugin to Plugin, but that’s mostly because there’s no (sanctioned?) Plugin script type.
I can’t think of any other globals that this could be applying to.

Thank you for pointing out my misunderstanding on this. Yeah I just realized that game and workspace being any are the issue here

2 Likes

That’s a bit weird that those global have the type of any because I’ve just checked it and they do have their types based on the datamodel. Maybe your studio is broken somehow?

1 Like

Make it so that in

for i,v in pairs(table) do

end

the table at the end of the loop gets table.clear executed on it if there aren’t any outside references to it.

1 Like

What would be your use case? You can easily just write table.clear after the loop

1 Like

Yeah i see. It’s just memory managing

1 Like

I haven’t see autocomplete improvements where we able to do this

type Direction = "north" | "south" | "east" | "west"

local a: {[Direction]: boolean} = {[^] = true}
local b: {[Direction]: boolean} = {["^"]}
local b: {[Direction]: boolean} = {^}

Or did I not get something?

1 Like