From 49644abc5992cc1b2a45682044eb8941e0cc89bb Mon Sep 17 00:00:00 2001 From: James <85808999+TheGreatSageEqualToHeaven@users.noreply.github.com> Date: Thu, 8 Feb 2024 05:49:02 +0000 Subject: [PATCH 01/12] Create syntax-if-statements-initializers.md --- docs/syntax-if-statements-initializers.md | 93 +++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 docs/syntax-if-statements-initializers.md diff --git a/docs/syntax-if-statements-initializers.md b/docs/syntax-if-statements-initializers.md new file mode 100644 index 00000000..c5ebb1ac --- /dev/null +++ b/docs/syntax-if-statements-initializers.md @@ -0,0 +1,93 @@ +# Initializers in if statements + +# Summary + +Introduce an initializer expression that declares and initializes variables in if statements. + +# Motivation + +Initializers can improve code clarity and reduce scope pollution by allowing developers to declare variables in the conditions of if statements. ⁤⁤ Declaring variables at the point of use in if statements for the scope of the if statement's block simplifies code, leading to better readability and understanding of program logic. By limiting the scope of variables to the condition's pertaining block, the risk of unintended variable reuse and naming conflicts is reduced. ⁤ + +The reduced scope pollution improves register space in extreme cases (or auto-generated code) where developers have many variables defined and have to work around register limits by reducing the register size. In some scenarios, especially on Roblox, initializing variables within if statements can lead to improved performance and reduced complexity by avoiding unnecessary calls by developers. ⁤ A common paradigm used by Roblox developers is to use `Instance:FindFirstChild` in their condition and then access it afterwards instead of keeping an existing variable around in an existing scope, polluting it. + +# Design + +If statements with initializers must match (following the Luau grammar) `'if' 'local' bindinglist ['=' explist] 'then'` and `'local' bindinglist ['=' explist] where exp 'then'` syntax. Parenthesis are also allowed around the initializer for consistency with other expressions. The variables declared by the initializer are only available to the block of that condition and will be undefined to the `elseif` conditions and blocks as well as the `else` block; any code after the if statement won't have the variables defined either. + +In the former case, the value of the first declared variable will be checked. + +Example: + +```lua +local function foo() + return true +end + +if local b = foo() then + print(b) +else + print("false") +end +``` + +`Output: true` + +In the latter case, the `exp` condition is checked rather than the initializer. + +Example: + +```lua +local function foo() + return true +end + +if local b = foo() where b == false then + print(b) +else + print("true") +end +``` + +`Output: true` + +When declaring multiple values inside of a condition, only the first condition will be evaluated. + +Example: + +```lua +local function foo() + return true, false +end + +if local a,b = foo() then + print'Hello World, from Luau!' +end +``` + +`Output: Hello World, from Luau!` + +# Drawbacks + +Parser recovery may be more fragile due to the `local` keyword. + +Initializers increase the complexity of the language syntax and may obscure control flow in complex conditions. + +# Alternatives + +A different keyword or token can be used in place of `where`. + +Introducing a new contextual keyword can introduce complexity in the parser, and a different existing keyword like `in` or `do` could be used instead. + +```lua +if local a, b = foo() in b > a then + print'Hello World, from Luau!' +end +``` + +While Luau is a verbose language that uses keywords for the majority of its syntax, another approach is using semicolons as a separator. This can work well because statements can use semicolons as a separator, which will retain consistency with the language. + +```lua +if local a, b = foo(); b > a then + print'Hello World, from Luau!' +end +``` From 8ecc5cf1e9a11c3be278e7b5e081c97d94fc67b9 Mon Sep 17 00:00:00 2001 From: James <85808999+TheGreatSageEqualToHeaven@users.noreply.github.com> Date: Thu, 8 Feb 2024 05:52:10 +0000 Subject: [PATCH 02/12] Small fix --- docs/syntax-if-statements-initializers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/syntax-if-statements-initializers.md b/docs/syntax-if-statements-initializers.md index c5ebb1ac..9df46a8c 100644 --- a/docs/syntax-if-statements-initializers.md +++ b/docs/syntax-if-statements-initializers.md @@ -8,7 +8,7 @@ Introduce an initializer expression that declares and initializes variables in i Initializers can improve code clarity and reduce scope pollution by allowing developers to declare variables in the conditions of if statements. ⁤⁤ Declaring variables at the point of use in if statements for the scope of the if statement's block simplifies code, leading to better readability and understanding of program logic. By limiting the scope of variables to the condition's pertaining block, the risk of unintended variable reuse and naming conflicts is reduced. ⁤ -The reduced scope pollution improves register space in extreme cases (or auto-generated code) where developers have many variables defined and have to work around register limits by reducing the register size. In some scenarios, especially on Roblox, initializing variables within if statements can lead to improved performance and reduced complexity by avoiding unnecessary calls by developers. ⁤ A common paradigm used by Roblox developers is to use `Instance:FindFirstChild` in their condition and then access it afterwards instead of keeping an existing variable around in an existing scope, polluting it. +The reduced scope pollution improves register space in extreme cases (or auto-generated code) where developers have many variables defined and have to work around register limits by reducing the register size. In some scenarios, especially on Roblox, initializing variables within if statements can lead to improved performance and reduced complexity by avoiding unnecessary calls by developers. ⁤ A common paradigm used by Roblox developers is to use `Instance:FindFirstChild` in their condition and then access it afterwards instead of keeping an existing variable around in the new scope, polluting the existing scope. # Design From 7615e25ed03e1cb2151dcf75edd80da7d7b4c0f4 Mon Sep 17 00:00:00 2001 From: James <85808999+TheGreatSageEqualToHeaven@users.noreply.github.com> Date: Thu, 8 Feb 2024 06:01:06 +0000 Subject: [PATCH 03/12] Update syntax-if-statements-initializers.md --- docs/syntax-if-statements-initializers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/syntax-if-statements-initializers.md b/docs/syntax-if-statements-initializers.md index 9df46a8c..cfaecc72 100644 --- a/docs/syntax-if-statements-initializers.md +++ b/docs/syntax-if-statements-initializers.md @@ -84,7 +84,7 @@ if local a, b = foo() in b > a then end ``` -While Luau is a verbose language that uses keywords for the majority of its syntax, another approach is using semicolons as a separator. This can work well because statements can use semicolons as a separator, which will retain consistency with the language. +While Luau is a verbose language that uses keywords for the majority of its syntax, another approach is using semicolons as a separator. This can work well because statements can use semicolons as a separator, which will retain consistency with the language. The same can be said for the comma, which would be consistent with for loop syntax. ```lua if local a, b = foo(); b > a then From 33a5933eb910dbd5fa220adb7a825e9efc39fc7c Mon Sep 17 00:00:00 2001 From: James <85808999+TheGreatSageEqualToHeaven@users.noreply.github.com> Date: Thu, 8 Feb 2024 06:03:22 +0000 Subject: [PATCH 04/12] Update syntax-if-statements-initializers.md --- docs/syntax-if-statements-initializers.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/syntax-if-statements-initializers.md b/docs/syntax-if-statements-initializers.md index cfaecc72..27ca3c00 100644 --- a/docs/syntax-if-statements-initializers.md +++ b/docs/syntax-if-statements-initializers.md @@ -50,7 +50,7 @@ end `Output: true` -When declaring multiple values inside of a condition, only the first condition will be evaluated. +When declaring multiple values inside of a condition, all of the variables will be evaluated. Example: @@ -60,6 +60,7 @@ local function foo() end if local a,b = foo() then +else print'Hello World, from Luau!' end ``` From 927dfed81ff72a4d6aa8f9fcef48049f0da89ffa Mon Sep 17 00:00:00 2001 From: James <85808999+TheGreatSageEqualToHeaven@users.noreply.github.com> Date: Thu, 15 Feb 2024 18:11:15 +0000 Subject: [PATCH 05/12] Update syntax-if-statements-initializers.md --- docs/syntax-if-statements-initializers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/syntax-if-statements-initializers.md b/docs/syntax-if-statements-initializers.md index 27ca3c00..e1f0026b 100644 --- a/docs/syntax-if-statements-initializers.md +++ b/docs/syntax-if-statements-initializers.md @@ -12,7 +12,7 @@ The reduced scope pollution improves register space in extreme cases (or auto-ge # Design -If statements with initializers must match (following the Luau grammar) `'if' 'local' bindinglist ['=' explist] 'then'` and `'local' bindinglist ['=' explist] where exp 'then'` syntax. Parenthesis are also allowed around the initializer for consistency with other expressions. The variables declared by the initializer are only available to the block of that condition and will be undefined to the `elseif` conditions and blocks as well as the `else` block; any code after the if statement won't have the variables defined either. +If statements with initializers must match (following the Luau grammar) `'if' 'local' bindinglist ['=' explist] 'then'` and `'local' bindinglist ['=' explist] where exp 'then'` syntax. The variables declared by the initializer are only available to the block of that condition and will be undefined to the `elseif` conditions and blocks as well as the `else` block; any code after the if statement won't have the variables defined either. In the former case, the value of the first declared variable will be checked. From 32fd23e41668e0bf845807485e9697d1757acd65 Mon Sep 17 00:00:00 2001 From: James <85808999+TheGreatSageEqualToHeaven@users.noreply.github.com> Date: Thu, 15 Feb 2024 18:25:03 +0000 Subject: [PATCH 06/12] Update syntax-if-statements-initializers.md --- docs/syntax-if-statements-initializers.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/syntax-if-statements-initializers.md b/docs/syntax-if-statements-initializers.md index e1f0026b..344c5216 100644 --- a/docs/syntax-if-statements-initializers.md +++ b/docs/syntax-if-statements-initializers.md @@ -67,6 +67,26 @@ end `Output: Hello World, from Luau!` +If statement initializers are also allowed in `elseif` conditions. + +Example: + +```lua +local a = false +local function foo() + local b = a + a = true + return b +end + +if local a = foo() then +elseif local b = foo() then + print(b) +end +``` + +`Output: true` + # Drawbacks Parser recovery may be more fragile due to the `local` keyword. From 08ce8e240a7db0ecf4510a25b1237835b3c32230 Mon Sep 17 00:00:00 2001 From: James <85808999+TheGreatSageEqualToHeaven@users.noreply.github.com> Date: Sat, 17 Feb 2024 10:51:45 +0000 Subject: [PATCH 07/12] Update docs/syntax-if-statements-initializers.md Co-authored-by: Alexander McCord <11488393+alexmccord@users.noreply.github.com> --- docs/syntax-if-statements-initializers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/syntax-if-statements-initializers.md b/docs/syntax-if-statements-initializers.md index 344c5216..624c45a2 100644 --- a/docs/syntax-if-statements-initializers.md +++ b/docs/syntax-if-statements-initializers.md @@ -50,7 +50,7 @@ end `Output: true` -When declaring multiple values inside of a condition, all of the variables will be evaluated. +When declaring multiple values inside of a condition, all of the variables will be tested for truthiness. Example: From fad18b4680b00970900bb31cab942de295e84923 Mon Sep 17 00:00:00 2001 From: James <85808999+TheGreatSageEqualToHeaven@users.noreply.github.com> Date: Sat, 17 Feb 2024 11:04:17 +0000 Subject: [PATCH 08/12] Update syntax-if-statements-initializers.md --- docs/syntax-if-statements-initializers.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/syntax-if-statements-initializers.md b/docs/syntax-if-statements-initializers.md index 624c45a2..35172063 100644 --- a/docs/syntax-if-statements-initializers.md +++ b/docs/syntax-if-statements-initializers.md @@ -6,13 +6,13 @@ Introduce an initializer expression that declares and initializes variables in i # Motivation -Initializers can improve code clarity and reduce scope pollution by allowing developers to declare variables in the conditions of if statements. ⁤⁤ Declaring variables at the point of use in if statements for the scope of the if statement's block simplifies code, leading to better readability and understanding of program logic. By limiting the scope of variables to the condition's pertaining block, the risk of unintended variable reuse and naming conflicts is reduced. ⁤ +Initializers can improve code clarity and reduce scope pollution by allowing developers to declare variables in the conditions of if statements. ⁤⁤ Declaring variables at the point of use in if statements for the scope of the if statement's blocks simplifies code, leading to better readability and understanding of program logic. By limiting the scope of variables to the if statement's blocks, the risk of unintended variable reuse and naming conflicts is reduced. ⁤ The reduced scope pollution improves register space in extreme cases (or auto-generated code) where developers have many variables defined and have to work around register limits by reducing the register size. In some scenarios, especially on Roblox, initializing variables within if statements can lead to improved performance and reduced complexity by avoiding unnecessary calls by developers. ⁤ A common paradigm used by Roblox developers is to use `Instance:FindFirstChild` in their condition and then access it afterwards instead of keeping an existing variable around in the new scope, polluting the existing scope. # Design -If statements with initializers must match (following the Luau grammar) `'if' 'local' bindinglist ['=' explist] 'then'` and `'local' bindinglist ['=' explist] where exp 'then'` syntax. The variables declared by the initializer are only available to the block of that condition and will be undefined to the `elseif` conditions and blocks as well as the `else` block; any code after the if statement won't have the variables defined either. +If statements with initializers must match (following the Luau grammar) `'if' 'local' bindinglist ['=' explist] 'then'` and `'local' bindinglist ['=' explist] where exp 'then'` syntax. The variables declared by an initializer are only available to the if statement's blocks; any code after the if statement won't have the variables defined. In the former case, the value of the first declared variable will be checked. @@ -24,13 +24,13 @@ local function foo() end if local b = foo() then - print(b) + print(b, "truthy block") else - print("false") + print(b, "falsy block") end ``` -`Output: true` +`Output: true truthy block` In the latter case, the `exp` condition is checked rather than the initializer. @@ -42,15 +42,15 @@ local function foo() end if local b = foo() where b == false then - print(b) + print(b, "truthy block") else - print("true") + print(b, "falsy block") end ``` -`Output: true` +`Output: true falsy block` -When declaring multiple values inside of a condition, all of the variables will be tested for truthiness. +When declaring multiple values inside of an initializer, the `where` clause is required. Example: @@ -59,7 +59,7 @@ local function foo() return true, false end -if local a,b = foo() then +if local a,b = foo() where a and b then else print'Hello World, from Luau!' end From 614a6c26f2eced2c01b14e3b4e9cdd72a41405de Mon Sep 17 00:00:00 2001 From: James <85808999+TheGreatSageEqualToHeaven@users.noreply.github.com> Date: Mon, 19 Feb 2024 20:37:55 +0000 Subject: [PATCH 09/12] Update syntax-if-statements-initializers.md --- docs/syntax-if-statements-initializers.md | 42 ++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/docs/syntax-if-statements-initializers.md b/docs/syntax-if-statements-initializers.md index 35172063..85680dd3 100644 --- a/docs/syntax-if-statements-initializers.md +++ b/docs/syntax-if-statements-initializers.md @@ -8,7 +8,47 @@ Introduce an initializer expression that declares and initializes variables in i Initializers can improve code clarity and reduce scope pollution by allowing developers to declare variables in the conditions of if statements. ⁤⁤ Declaring variables at the point of use in if statements for the scope of the if statement's blocks simplifies code, leading to better readability and understanding of program logic. By limiting the scope of variables to the if statement's blocks, the risk of unintended variable reuse and naming conflicts is reduced. ⁤ -The reduced scope pollution improves register space in extreme cases (or auto-generated code) where developers have many variables defined and have to work around register limits by reducing the register size. In some scenarios, especially on Roblox, initializing variables within if statements can lead to improved performance and reduced complexity by avoiding unnecessary calls by developers. ⁤ A common paradigm used by Roblox developers is to use `Instance:FindFirstChild` in their condition and then access it afterwards instead of keeping an existing variable around in the new scope, polluting the existing scope. +The reduced scope pollution improves register space in extreme cases (or auto-generated code) where developers have many variables defined and have to work around register limits by reducing the register size. In some scenarios, especially on Roblox, initializing variables within if statements can lead to improved performance and reduced complexity by avoiding unnecessary calls by developers. ⁤ A common paradigm used by Roblox developers is to use `Instance:FindFirstChild` in their condition and then access it afterwards instead of keeping an existing variable around in the new scope, polluting the existing scope. + +Another benefit provided by initializers is being able to compact verbose guard clauses into a singular if statement. + +Example: + +```lua +function PlayerControl:GetEntities() + if self.RemovedSelf then + return self.Instances + end + + local entity = self:TryGetEntity() + if entity == nil then + return self.Instances + end + + local index = table.find(self.Instances, entity.Root) + if index == nil then + return self.Instances + end + + table.remove(self.Instances, index) + self.RemovedSelf = true + + return self.Instances +end +``` + +```lua +function PlayerControl:GetEntities() + if self.RemovedSelf then + elseif local entity = self:TryGetEntity() where entity == nil then + elseif local index = table.find(self.Instances, entity.Root) then + table.remove(self.Instances, index) + self.RemovedSelf = true + end + + return self.Instances +end +``` # Design From b4e09afb9edbc5392d4ff75c7090768d7eb2ab7f Mon Sep 17 00:00:00 2001 From: James <85808999+TheGreatSageEqualToHeaven@users.noreply.github.com> Date: Wed, 20 Mar 2024 19:28:32 +0000 Subject: [PATCH 10/12] Update syntax-if-statements-initializers.md --- docs/syntax-if-statements-initializers.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/docs/syntax-if-statements-initializers.md b/docs/syntax-if-statements-initializers.md index 85680dd3..dc25961c 100644 --- a/docs/syntax-if-statements-initializers.md +++ b/docs/syntax-if-statements-initializers.md @@ -52,7 +52,24 @@ end # Design -If statements with initializers must match (following the Luau grammar) `'if' 'local' bindinglist ['=' explist] 'then'` and `'local' bindinglist ['=' explist] where exp 'then'` syntax. The variables declared by an initializer are only available to the if statement's blocks; any code after the if statement won't have the variables defined. +If statements with initializers must match the below grammar. The variables declared by an initializer are only available to the if statement's blocks; any code after the if statement won't have the variables defined. + +```diff + stat = varlist '=' explist | + ... + 'repeat' block 'until' exp | +- 'if' exp 'then' block {'elseif' exp 'then' block} ['else' block] 'end' | ++ 'if' cond 'then' block {'elseif' cond 'then' block} ['else' block] 'end' | + 'for' binding '=' exp ',' exp [',' exp] 'do' block 'end' | + ... + +- ifelseexp = 'if' exp 'then' exp {'elseif' exp 'then' exp} 'else' exp ++ ifelseexp = 'if' cond 'then' exp {'elseif' cond 'then' exp} 'else' exp + ++ cond = 'local' binding '=' exp ['where' exp] | ++ 'local' bindinglist '=' explist 'where' exp | ++ exp +``` In the former case, the value of the first declared variable will be checked. From 548d4c8ba9a0268ad17e4e29e401ccea7b1ee34d Mon Sep 17 00:00:00 2001 From: James <85808999+TheGreatSageEqualToHeaven@users.noreply.github.com> Date: Sat, 13 Apr 2024 18:42:37 +0100 Subject: [PATCH 11/12] Update docs/syntax-if-statements-initializers.md Co-authored-by: Alexander McCord <11488393+alexmccord@users.noreply.github.com> --- docs/syntax-if-statements-initializers.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/syntax-if-statements-initializers.md b/docs/syntax-if-statements-initializers.md index dc25961c..1bfcc4f9 100644 --- a/docs/syntax-if-statements-initializers.md +++ b/docs/syntax-if-statements-initializers.md @@ -62,10 +62,6 @@ If statements with initializers must match the below grammar. The variables decl + 'if' cond 'then' block {'elseif' cond 'then' block} ['else' block] 'end' | 'for' binding '=' exp ',' exp [',' exp] 'do' block 'end' | ... - -- ifelseexp = 'if' exp 'then' exp {'elseif' exp 'then' exp} 'else' exp -+ ifelseexp = 'if' cond 'then' exp {'elseif' cond 'then' exp} 'else' exp - + cond = 'local' binding '=' exp ['where' exp] | + 'local' bindinglist '=' explist 'where' exp | + exp From 544f99eed646793c1d45d1b8ae32d6a1463cbeca Mon Sep 17 00:00:00 2001 From: aaron Date: Tue, 7 May 2024 14:07:51 -0700 Subject: [PATCH 12/12] Update syntax-if-statements-initializers.md `where` ~> `in` --- docs/syntax-if-statements-initializers.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/syntax-if-statements-initializers.md b/docs/syntax-if-statements-initializers.md index 1bfcc4f9..30eff392 100644 --- a/docs/syntax-if-statements-initializers.md +++ b/docs/syntax-if-statements-initializers.md @@ -40,7 +40,7 @@ end ```lua function PlayerControl:GetEntities() if self.RemovedSelf then - elseif local entity = self:TryGetEntity() where entity == nil then + elseif local entity = self:TryGetEntity() in entity == nil then elseif local index = table.find(self.Instances, entity.Root) then table.remove(self.Instances, index) self.RemovedSelf = true @@ -62,8 +62,8 @@ If statements with initializers must match the below grammar. The variables decl + 'if' cond 'then' block {'elseif' cond 'then' block} ['else' block] 'end' | 'for' binding '=' exp ',' exp [',' exp] 'do' block 'end' | ... -+ cond = 'local' binding '=' exp ['where' exp] | -+ 'local' bindinglist '=' explist 'where' exp | ++ cond = 'local' binding '=' exp ['in' exp] | ++ 'local' bindinglist '=' explist 'in' exp | + exp ``` @@ -94,7 +94,7 @@ local function foo() return true end -if local b = foo() where b == false then +if local b = foo() in b == false then print(b, "truthy block") else print(b, "falsy block") @@ -103,7 +103,7 @@ end `Output: true falsy block` -When declaring multiple values inside of an initializer, the `where` clause is required. +When declaring multiple values inside of an initializer, the `in` clause is required. Example: @@ -112,7 +112,7 @@ local function foo() return true, false end -if local a,b = foo() where a and b then +if local a, b = foo() in a and b then else print'Hello World, from Luau!' end @@ -148,12 +148,12 @@ Initializers increase the complexity of the language syntax and may obscure cont # Alternatives -A different keyword or token can be used in place of `where`. +A different keyword or token can be used in place of `in`. -Introducing a new contextual keyword can introduce complexity in the parser, and a different existing keyword like `in` or `do` could be used instead. +Rather than use `in`, we could introduce a new contextual keyword, or use a different existing keyword like `do`. ```lua -if local a, b = foo() in b > a then +if local a, b = foo() do b > a then print'Hello World, from Luau!' end ```