Conversation
|
I would really desperately want to see this - my code is littered with places this would be incredible - but a similar idea I mentioned a while back about "return in expressions" got shot down. I really hope there is an expression of this design that can be agreed upon. |
|
|
||
| ## Summary | ||
|
|
||
| The `or return` syntax is a more compact, understandable way to end execution if an expression evaluates to `nil`. |
There was a problem hiding this comment.
Inconsistent with usual truthiness test. What if the expression evaluates to false?
There was a problem hiding this comment.
Probably should have clarified here - the main use case is for expressions that can return nil, but the already-established truthiness rules would be respected.
|
Would |
| local humanoid = partParent:FindFirstChild("Humanoid") or return | ||
|
|
||
| humanoid.Health = 0 |
There was a problem hiding this comment.
This is just if local which is proposed in a different RFC, so this isn't a strong motivator.
There was a problem hiding this comment.
if local does also solve this problem, but it also creates a new indentation level which is counter to the goals of this RFC.
| ``` | ||
| and compact it into a single, simpler statement: | ||
| ```lua | ||
| local var = foo() or return false |
There was a problem hiding this comment.
Doesn't compose with multrets.
There was a problem hiding this comment.
Why not? As far as I can tell, the only possible reason it wouldn't is if commas were used somewhere else in the expression, which is solved by using a pair of parentheses.
|
|
|
|
||
| `or return` can be implemented as a binary operation with an optional right side, returning `nil` if no value is given. | ||
|
|
||
| The parsing step would be fairly simple as well. Since the new syntax is a way to express an already-supported feature more concisely, it can be transformed back into its original form in the parser. This means no changes are necessitated on the compiler side. If specified in the middle of an expression, it can temporarily store the value on its left side to perform the nil check, then compute the rest of the expression using the original copy. For example, |
There was a problem hiding this comment.
Parser can't rewrite the AST beyond its local context, so AST changes are required for this feature to work.
There was a problem hiding this comment.
That's understandable. Is there some form of AST expansion/lowering pass that this could fit into, or would this need to be supported in the compiler as well?
I did consider allowing |
|
Here's an idea, local humanoid = partParent:FindFirstChild("Humanoid") or else returnIt's never valid Luau to have the And hey, it reads like posh English, that's fun too :) |
Feel like it should be orelse to be consistent with elseif, sure its another operator (I think operator is the correct term?) but I don't think anyones naming variables orelse |
|
Also this would have to be or return ... endbecause otherwise this would return the result of PivotTo, as we can't have implicit whitespace or return
model:PivotTo(CFrame.Identity) |
ccuser44
left a comment
There was a problem hiding this comment.
This doesn't make to have in Luau and is syntactical bloat. And frankly, just looks kinda ugly.
|
I personally do not like the structure of this syntax. I would prefer something along the lines C# with their Null-conditional Operators for some of the use cases listed. Roblox Type Explanations
This section is for Luau users unfamiliar with Roblox functions and conventions:
local human: Humanoid? = hit.Parent?:FindFirstChildOfClass("Humanoid");
--[[
eq:
local human: Humanoid? = nil;
do
local temp = hit.Parent;
if temp ~= nil then human = temp:FindFirstChildOfClass("Humanoid"); end
end
]]
-- ...
local name: string? = object:FindFirstChildOfClass("ObjectValue")?.Name;
--[[
eq:
local name: string? = nil;
do
local temp = object:FindFirstChildOfClass("ObjectValue");
if temp ~= nil then name = temp.Name; end
end
]]
-- ...
local getFirst = list[key]?[1];
--[[
eq:
local getFirst = nil;
do
local temp = list[key];
if temp ~= nil then getFirst = temp[1]; end
end
]]
In the above cases, types that are null-conditional, i.e. -- sealed table '{a:{b:string}}'
local t = {a = {b = "some text"}};
local text = t.a?.b; -- text: string
-- 't.a' is '{b:string}' and not 'nil' / nullable, so regular 'a.b' indexing is used: 'index<a, 'b'>'
local grandParent = part.Parent?.Parent; -- grandParent: Instance?
-- 'part.Parent' is 'Instance?' and not 'Instance', so null-conditional indexing is used, resulting in 'index<Instance, "Parent">?' or 'Instance??' aka 'Instance?'
-- in this case, 'part.Parent.Parent' by contrast will emit a 'Type Error' since 'index<Instance?, "Parent">' is invalidOr, the following syntax for short circuit evaluations, inspired by Initializers in if statements: local value = if local temp = somefunction() in temp ~= nil then temp else default();
-- or alternatively
local value = if local temp = somefunction() in temp == nil then default() else temp;In both cases, |
|
@Wunder-Wulfe Would probably be better that you make a RFC for this, probably paired with null assertion operators as well |
|
We already had the safe navigation operator RFC and we had to veto it out. |
|
Not sure if it's ever been specifically considered, but we could adapt Rust's local cat = animals_folder:FindFirstChild("Cat") else return nil
print(cat.Position) -- indexing .Position doesn't error because cat can't be nil
-- runtime equivalent to
local cat = animals_folder:FindFirstChild("Cat")
if cat == nil then
return nil
end
-- as default value
local cat = animals_folder:FindFirstChild("Cat") else default_cat
-- equivalent to
local cat = animals_folder:FindFirstChild("Cat")
if cat == nil then
cat = default_cat
end
|
You've just described |
iirc |
A small syntax extension to make handling
nilvalues easier and more concise.Rendered