After doing today’s LeetCode daily, I was wondering if Luau might potentially implement something like Python’s walrus operator. Here’s what little I could find about it:
Will developers find it useful to be able to initialize variables and evaluate them in the same line, in a readable manner? How come this doesn’t have much attention? Is this not an improvement to writing game development code?
It will complicate the bytecode and parser for zero real gain
Currently control-flow expressions in Luau are simple: if expects a boolean expression while expects a boolean expression
Introducing local definitions there forces the language to support a new special grammar only for these spots.
This means:
1. New parser rules
2. New bytecode patterns
3. New scope semantics
It would also break current scoping model
if local a = expr() then
print(a) --// should this be allowed? yes?
end
print(a) --// should this be allowed? no?
Luau would need a unique hidden scope, that only exists before the if-block but not after.
It will also create confusing code, that will encourage hiding logic.
This is readable:
local v = getValue()
if v then
print("exists")
end
This is not:
if local v = getValue() then
print("exists")
end
Basically you gain no performance gain from local definitions in control flow.
Luau is micro-optimized bytecode language, with each opcode being used as much as possible, adding more VM work just for “confusing” readability is not going to be a good thing at all.
local Players = game:GetService("Players")
local function check_if_player_hits(hit)
local hit_parent = hit.Parent
local Humanoid = hit_parent:FindFirstChildOfClass("Humanoid")
if Humanoid and Players:GetPlayerFromCharacter(hit_parent) then
local Player = Players:GetPlayerFromCharacter(hit_parent)
print("Touched")
end
end
Proposed
local Players = game:GetService("Players")
local function check_if_player_hits(hit)
local hit_parent = hit.Parent
local Humanoid = hit_parent:FindFirstChildOfClass("Humanoid")
if Humanoid and (local Player := Players:GetPlayerFromCharacter(hit_parent)) then
print("Touched")
end
end
Where I purposely want Player and Humanoid to be in different scopes
Could I write this better without repeating Players:GetPlayerFromCharacter(...) while preserving this scope requirement?
it’s important how your code works and how you think
assuming your function only detects hits for a Player, you could first check if it’s a player, then getting the humanoid, which will save you unnoticeable performance.
local function check_if_hits(hit)
local hit_parent = hit.Parent
if Players:GetPlayerFromCharacter(hit_parent) then
local Humanoid = hit_parent:FindFirstChildOfClass("Humanoid")
print("Touched")
end
end
now Humanoid is referenced only if it’s an actual Player.
if you want to save any tiny bit of ms of performance, you need to do every micro-optimization
Why would you need player in the more specific scope though? Keeping stuff in the most specific scope is generally ideal, but I have a hard time understanding why you wouldn’t just do this
local Player = Players:GetPlayerFromCharacter(hit_parent)
if Humanoid and Player then
print("Touched")
end
It’s not really in too high a scope for what it is
I can’t conjure up a better example at the moment, but I do get that it’s not natural to want to keep a Player at a more specific scope than Humanoid
I just think that the repetition of checking and then initializing after the check is a redundancy that could be removed in other code examples (that I can’t think of atm)
I know I’ve definitely done this before but I cannot think of a time I’ve done this that I wasn’t able to rewrite it in some way as it is above. Maybe I’m just forgetting a random edge case though.
I would say no.
local Player = Players:GetPlayerFromCharacter(hit_parent)
if Humanoid and Player then
print("Touched")
end
vs
do
if Humanoid and Players:GetPlayerFromCharacter(hit_parent) then
print("Touched")
end
end
Though at this point we should probably just be doing
if Humanoid then
local Player = Players:GetPlayerFromCharacter(hit_parent)
if Player then
print("Touched")
end
end
local function check_if_player_hits(hit)
local hit_parent = hit.Parent
do
local Humanoid = hit_parent:FindFirstChildOfClass("Humanoid")
if not Humanoid then
return
end
do
local Player = Players:GetPlayerFromCharacter(hit_parent)
if not Player then
return
end
print("Touched")
end
end
end
But again, you don’t need to code like this, you can code a perfectly good code without using do .. end blocks.
I’d do this:
local function check_if_player_hits(hit)
local hit_parent = hit.Parent
local Player = Players:GetPlayerFromCharacter(hit_parent)
if not Player then return end
local Humanoid = hit_parent:FindFirstChildOfClass("Humanoid")
if not Humanoid then return end
print("Touched")
end
Now Humanoid will only be referenced if it’s an actual Players body part