From 9b84a137d7fd16f3f573a64b3eed3e6bccb51642 Mon Sep 17 00:00:00 2001 From: InfraredGod <150057615+InfraredGodYT@users.noreply.github.com> Date: Mon, 30 Mar 2026 00:23:14 -0500 Subject: [PATCH 1/8] Create table-comprehension.md --- docs/table-comprehension.md | 217 ++++++++++++++++++++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 docs/table-comprehension.md diff --git a/docs/table-comprehension.md b/docs/table-comprehension.md new file mode 100644 index 00000000..eb87f81a --- /dev/null +++ b/docs/table-comprehension.md @@ -0,0 +1,217 @@ + +# Table Comprehension + +## Summary + +Add table comprehension syntax for constructing arrays and maps using inline iteration and optional filtering expressions. + +--- + +## Motivation + +Constructing tables using loops is a very common pattern in Luau. However, this often results in verbose and mutation-heavy code. + +For example: + +```lua +local result = {} +for _, v in ipairs(items) do + if v > 1 then + table.insert(result, v * 2) + end +end +```` + +This approach has several drawbacks: + +* Requires explicit mutation (`table.insert`) +* Introduces boilerplate for simple transformations +* Obscures intent for simple mapping/filtering operations +* May be harder for the compiler to optimize + +A table comprehension syntax would allow this to be written more concisely: + +```lua +local result = {v * 2 for _, v in ipairs(items) if v > 1} +``` + +This pattern is widely used in other languages (e.g. Python) and Lua-adjacent languages (e.g. MoonScript), demonstrating its usefulness for data transformation. + +--- + +## Design + +### Syntax + +Two forms of table comprehension are proposed: + +#### Array-style comprehension + +```lua +{expression for variables in iterator [if condition]} +``` + +Examples: + +```lua +local doubled = {v * 2 for _, v in ipairs(items)} +local filtered = {v for _, v in ipairs(items) if v > 1} +``` + +--- + +#### Map-style comprehension + +```lua +{[key] = value for variables in iterator [if condition]} +``` + +Examples: + +```lua +local map = {[k] = v for k, v in pairs(t)} +``` + +--- + +### Grammar + +The following modification to the Luau grammar is proposed: + +```diff +tableconstructor ::= '{' [fieldlist | comprehension] '}' + ++ comprehension ::= exp 'for' namelist 'in' explist ['if' exp] +``` + +--- + +### Semantics + +#### Evaluation order + +* The iterator expression is evaluated first +* The comprehension executes similarly to a `for` loop +* The optional `if` condition filters elements + +--- + +#### Array behavior + +```lua +{expr for _, v in ipairs(t)} +``` + +is equivalent to: + +```lua +local result = {} +for _, v in ipairs(t) do + result[#result + 1] = expr +end +``` + +--- + +#### Map behavior + +```lua +{[k] = v for k, v in pairs(t)} +``` + +is equivalent to: + +```lua +local result = {} +for k, v in pairs(t) do + result[k] = v +end +``` + +If duplicate keys are produced, the last assignment takes precedence. + +--- + +#### Scope + +* Variables introduced in the comprehension are scoped to the comprehension +* They are not visible outside the expression + +--- + +#### Nil handling + +If the comprehension expression evaluates to `nil`, the value is still inserted into the table (consistent with standard table behavior). + +--- + +### Type Semantics + +* Array comprehensions infer type `{T}` where `T` is the type of the expression +* Map comprehensions infer type `{[K]: V}` based on key and value expressions +* The type checker should treat comprehensions similarly to their desugared loop equivalents + +--- + +### Language implementation + +This feature can be implemented entirely in the compiler by lowering comprehensions into equivalent `for` loops. + +For example: + +```lua +{expr for _, v in ipairs(t)} +``` + +can be compiled into: + +```lua +local result = {} +for _, v in ipairs(t) do + result[#result + 1] = expr +end +``` + +This approach: + +* Requires no changes to the Luau VM +* Reuses existing loop semantics +* Allows for future optimizations (e.g. preallocation) + +--- + +## Drawbacks + +* Adds new syntax to the language +* May be misused in complex or nested forms, reducing readability +* Not strictly necessary, as equivalent functionality already exists using loops + +--- + +## Alternatives + +### Existing loop-based approach + +```lua +local result = {} +for _, v in ipairs(items) do + table.insert(result, v * 2) +end +``` + +This is more verbose and mutation-based. + +--- + +### Helper functions + +Users may define utility functions for mapping/filtering, but these introduce additional abstraction and may reduce performance or clarity. + +--- + +## Prior Art + +* Python list/dict comprehensions +* MoonScript table comprehensions + +```` From 36676d833f91ba4ba4a07e28517a95746244ed99 Mon Sep 17 00:00:00 2001 From: InfraredGod <150057615+InfraredGodYT@users.noreply.github.com> Date: Mon, 30 Mar 2026 01:18:34 -0500 Subject: [PATCH 2/8] Update table-comprehension.md --- docs/table-comprehension.md | 235 ++++++++++++++++++------------------ 1 file changed, 116 insertions(+), 119 deletions(-) diff --git a/docs/table-comprehension.md b/docs/table-comprehension.md index eb87f81a..f93470e1 100644 --- a/docs/table-comprehension.md +++ b/docs/table-comprehension.md @@ -1,217 +1,214 @@ - # Table Comprehension ## Summary -Add table comprehension syntax for constructing arrays and maps using inline iteration and optional filtering expressions. - ---- +Table comprehension is a specific type of syntax which allows you to construct tables using inline iteration and optional filtering expressions. ## Motivation -Constructing tables using loops is a very common pattern in Luau. However, this often results in verbose and mutation-heavy code. - -For example: +Constructing tables using loops is a very common practice in Luau, particularly for transforming and filtering collections. However, the current approach is verbose, even for simple transformations. +Examples: ```lua -local result = {} -for _, v in ipairs(items) do - if v > 1 then - table.insert(result, v * 2) - end +-- Arrays +local numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} +local doubled = {} +for i, v in ipairs(numbers) do + doubled[i] = v * 2 end -```` -This approach has several drawbacks: - -* Requires explicit mutation (`table.insert`) -* Introduces boilerplate for simple transformations -* Obscures intent for simple mapping/filtering operations -* May be harder for the compiler to optimize +local evens = {} +for _, v in ipairs(numbers) do + if v % 2 == 0 then + table.insert(evens, v) + end +end -A table comprehension syntax would allow this to be written more concisely: +-- Maps +local playersByName = {} +for _, player in ipairs(game.Players:GetPlayers()) do + playersByName[player.Name] = player +end -```lua -local result = {v * 2 for _, v in ipairs(items) if v > 1} +local alivePlayers = {} +for _, player in ipairs(game.Players:GetPlayers()) do + if player.Health > 0 then + alivePlayers[player.UserId] = player + end +end ``` -This pattern is widely used in other languages (e.g. Python) and Lua-adjacent languages (e.g. MoonScript), demonstrating its usefulness for data transformation. +Patterns like these are especially common in Roblox development, especially in cases such as building lookup tables and filtering instances by state. + +However, this approach has the following drawbacks: +- Requires explicit mutation of tables (`table.insert`, index assignment) +- Introduces boilerplate for simple transformations +- Obscures intent by separating iteration, filtering, and assignment +- Increases the likelihood of indexing or insertion mistakes +- Lacks a concise expression form for common transformation patterns ---- +Additionally, these patterns are difficult for the compiler to recognize as a single transformation, which limits opportunities for optimization. +A table comprehension syntax allows these operations to be expressed as a single construct. While it does not inherently change runtime behavior, it provides the compiler with a clearer representation of the programmer’s intent, which may enable future optimizations such as preallocation or loop specialization. ## Design ### Syntax -Two forms of table comprehension are proposed: +Two forms of table comprehension are proposed. -#### Array-style comprehension - -```lua -{expression for variables in iterator [if condition]} -``` +#### Array-style +`{expr for namelist in explist [if condition]}` Examples: ```lua -local doubled = {v * 2 for _, v in ipairs(items)} -local filtered = {v for _, v in ipairs(items) if v > 1} +local doubled = {v * 2 for i, v in ipairs(numbers)} +local evens = {v for _, v in ipairs(numbers) if v % 2 == 0} ``` ---- - -#### Map-style comprehension - -```lua -{[key] = value for variables in iterator [if condition]} -``` +#### Map-style +`{[key] = value for namelist in explist [if condition]}` Examples: ```lua -local map = {[k] = v for k, v in pairs(t)} +local playersByName = { + [player.Name] = player + for _, player in ipairs(game.Players:GetPlayers()) +} +local alivePlayers = { + [player.UserId] = player + for _, player in ipairs(game.Players:GetPlayers()) + if player.Health > 0 +} ``` ---- - -### Grammar +Whitespace and line breaks follow standard Lua rules and do not affect parsing. Both inline and multiline forms are valid. -The following modification to the Luau grammar is proposed: - -```diff -tableconstructor ::= '{' [fieldlist | comprehension] '}' - -+ comprehension ::= exp 'for' namelist 'in' explist ['if' exp] -``` - ---- +Table comprehensions **cannot** be combined with standard table fields within the same constructor. ### Semantics #### Evaluation order -* The iterator expression is evaluated first -* The comprehension executes similarly to a `for` loop -* The optional `if` condition filters elements +- The iterator expression (`explist`) is evaluated once before iteration begins +- The comprehension executes with semantics equivalent to a `for` loop +- For each iteration: + - Loop variables are assigned + - The condition (if present) is evaluated + - If the condition evaluates to a truthy value (or is absent): + - The expression is evaluated + - The result is inserted into the table ---- +#### Scope -#### Array behavior +Variables introduced by the comprehension are scoped to the comprehension expression and are not visible outside of it, consistent with `for` loop semantics. -```lua -{expr for _, v in ipairs(t)} -``` +#### Array semantics + +`{expr for namelist in explist}` is equivalent to: ```lua local result = {} -for _, v in ipairs(t) do +for namelist in explist do result[#result + 1] = expr end ``` ---- +If `expr` evaluates to `nil`, the assignment behaves according to standard table semantics (`result[#result + 1] = nil`), which may result in holes in the array. -#### Map behavior +#### Map semantics -```lua -{[k] = v for k, v in pairs(t)} -``` +`{[key] = value for namelist in explist}` is equivalent to: ```lua local result = {} -for k, v in pairs(t) do - result[k] = v +for namelist in explist do + result[key] = value end ``` If duplicate keys are produced, the last assignment takes precedence. ---- - -#### Scope - -* Variables introduced in the comprehension are scoped to the comprehension -* They are not visible outside the expression - ---- - -#### Nil handling - -If the comprehension expression evaluates to `nil`, the value is still inserted into the table (consistent with standard table behavior). +If `value` evaluates to `nil`, the key is removed from the table, consistent with standard table behavior. ---- +#### Conditional filters -### Type Semantics +If a condition is provided: -* Array comprehensions infer type `{T}` where `T` is the type of the expression -* Map comprehensions infer type `{[K]: V}` based on key and value expressions -* The type checker should treat comprehensions similarly to their desugared loop equivalents +`{expr for namelist in explist if condition}` ---- - -### Language implementation - -This feature can be implemented entirely in the compiler by lowering comprehensions into equivalent `for` loops. - -For example: +it is equivalent to: ```lua -{expr for _, v in ipairs(t)} +local result = {} +for namelist in explist do + if condition then + result[#result + 1] = expr + end +end ``` -can be compiled into: +For map-style comprehensions with a condition: + +`{[key] = value for namelist in explist if condition}` + +is equivalent to: ```lua local result = {} -for _, v in ipairs(t) do - result[#result + 1] = expr +for namelist in explist do + if condition then + result[key] = value + end end ``` -This approach: - -* Requires no changes to the Luau VM -* Reuses existing loop semantics -* Allows for future optimizations (e.g. preallocation) +#### Iterator semantics ---- +The `namelist in explist` portion follows the same semantics as Luau `for` loops, supporting all valid iterator forms including `ipairs`, `pairs`, and custom iterators. ## Drawbacks -* Adds new syntax to the language -* May be misused in complex or nested forms, reducing readability -* Not strictly necessary, as equivalent functionality already exists using loops +Table comprehension introduces new syntax to the language, increasing overall language complexity. While the feature is designed to be minimal and consistent with existing `for` loop semantics, it adds another way to express iteration and table construction, which may impact readability for developers unfamiliar with the syntax. ---- +Comprehensions may also be overused in complex scenarios, leading to code that is harder to read compared to explicit loops. + +Additionally, this feature does not introduce new capabilities, as all functionality can already be expressed using existing `for` loops. Its primary benefit is improved expressiveness and conciseness. ## Alternatives -### Existing loop-based approach +### Status quo (loop-based construction) + +Continue using `for` loops to construct tables: ```lua local result = {} for _, v in ipairs(items) do - table.insert(result, v * 2) + if v > 1 then + table.insert(result, v * 2) + end end ``` -This is more verbose and mutation-based. - ---- +This approach is fully expressive and requires no new language features. However, it is more verbose and requires explicit mutation, which can obscure intent and introduce opportunities for errors. ### Helper functions -Users may define utility functions for mapping/filtering, but these introduce additional abstraction and may reduce performance or clarity. - ---- - -## Prior Art +Users may define utility functions for common patterns such as mapping or filtering: -* Python list/dict comprehensions -* MoonScript table comprehensions +```lua +local function map(t, fn) + local result = {} + for i, v in ipairs(t) do + result[i] = fn(v) + end + return result +end +``` -```` +While this reduces repetition, it introduces additional abstraction and may reduce clarity for simple transformations. It may also introduce overhead compared to inline constructs. From 4378bf71e29afcf31d02ea9a8c652e6b23fa43e5 Mon Sep 17 00:00:00 2001 From: InfraredGod <150057615+InfraredGodYT@users.noreply.github.com> Date: Mon, 30 Mar 2026 01:23:48 -0500 Subject: [PATCH 3/8] Update table-comprehension.md --- docs/table-comprehension.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/table-comprehension.md b/docs/table-comprehension.md index f93470e1..342192b6 100644 --- a/docs/table-comprehension.md +++ b/docs/table-comprehension.md @@ -172,6 +172,8 @@ end The `namelist in explist` portion follows the same semantics as Luau `for` loops, supporting all valid iterator forms including `ipairs`, `pairs`, and custom iterators. +### Grammar + ## Drawbacks Table comprehension introduces new syntax to the language, increasing overall language complexity. While the feature is designed to be minimal and consistent with existing `for` loop semantics, it adds another way to express iteration and table construction, which may impact readability for developers unfamiliar with the syntax. From 48b5fb50571e138954ed4a69afa9142d31d528f6 Mon Sep 17 00:00:00 2001 From: InfraredGod <150057615+InfraredGodYT@users.noreply.github.com> Date: Mon, 30 Mar 2026 01:35:09 -0500 Subject: [PATCH 4/8] Update table-comprehension.md --- docs/table-comprehension.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/table-comprehension.md b/docs/table-comprehension.md index 342192b6..dac7bffa 100644 --- a/docs/table-comprehension.md +++ b/docs/table-comprehension.md @@ -47,8 +47,8 @@ However, this approach has the following drawbacks: - Increases the likelihood of indexing or insertion mistakes - Lacks a concise expression form for common transformation patterns -Additionally, these patterns are difficult for the compiler to recognize as a single transformation, which limits opportunities for optimization. -A table comprehension syntax allows these operations to be expressed as a single construct. While it does not inherently change runtime behavior, it provides the compiler with a clearer representation of the programmer’s intent, which may enable future optimizations such as preallocation or loop specialization. +Additionally, these patterns are expressed using general-purpose loops, which may make it harder for the compiler to consistently identify them as table transformations. +A table comprehension syntax allows these operations to be expressed as a single construct. While it does not inherently change runtime behavior, it provides a more explicit representation of the programmer’s intent, which may enable future optimizations such as preallocation or loop specialization. ## Design From 3625ea49ca6be4f628afa18acfd84f0a733cea9b Mon Sep 17 00:00:00 2001 From: InfraredGod <150057615+InfraredGodYT@users.noreply.github.com> Date: Mon, 30 Mar 2026 01:35:53 -0500 Subject: [PATCH 5/8] Update table-comprehension.md --- docs/table-comprehension.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/table-comprehension.md b/docs/table-comprehension.md index dac7bffa..82513f09 100644 --- a/docs/table-comprehension.md +++ b/docs/table-comprehension.md @@ -2,7 +2,7 @@ ## Summary -Table comprehension is a specific type of syntax which allows you to construct tables using inline iteration and optional filtering expressions. +Add table comprehension syntax for constructing tables using inline iteration and optional filtering expressions. ## Motivation From 5f81d14a6fa3b978bcdda817db986e769d3edee4 Mon Sep 17 00:00:00 2001 From: InfraredGod <150057615+InfraredGodYT@users.noreply.github.com> Date: Mon, 30 Mar 2026 11:22:38 -0500 Subject: [PATCH 6/8] Update table-comprehension.md --- docs/table-comprehension.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/table-comprehension.md b/docs/table-comprehension.md index 82513f09..de3eee59 100644 --- a/docs/table-comprehension.md +++ b/docs/table-comprehension.md @@ -214,3 +214,26 @@ end ``` While this reduces repetition, it introduces additional abstraction and may reduce clarity for simple transformations. It may also introduce overhead compared to inline constructs. + + +### Standard library helpers (e.g. `table.map`) + +An alternative to new syntax is to provide standard library functions for common +patterns such as mapping and filtering: + +```lua +local result = table.map(items, function(v) + return v * 2 +end) +``` + +While a standard library function such as `table.map` could provide a reusable abstraction, +it would still rely on function calls and callbacks, which may reduce clarity for simple transformations. + +Additionally: +- The transformation logic is nested inside a function, making simple expressions less direct +- It separates iteration from table construction, rather than expressing both in a single construct +- It does not naturally support combining mapping and filtering without additional helper functions + +While such helpers may still be useful as complementary features, this RFC focuses on providing +a concise, inline syntax for expressing these patterns directly. From 5a00897e8a3afaf358ee06ace7fe974a81d56da4 Mon Sep 17 00:00:00 2001 From: InfraredGod <150057615+InfraredGodYT@users.noreply.github.com> Date: Mon, 30 Mar 2026 11:27:41 -0500 Subject: [PATCH 7/8] Update table-comprehension.md --- docs/table-comprehension.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/table-comprehension.md b/docs/table-comprehension.md index de3eee59..a8dc0171 100644 --- a/docs/table-comprehension.md +++ b/docs/table-comprehension.md @@ -172,8 +172,6 @@ end The `namelist in explist` portion follows the same semantics as Luau `for` loops, supporting all valid iterator forms including `ipairs`, `pairs`, and custom iterators. -### Grammar - ## Drawbacks Table comprehension introduces new syntax to the language, increasing overall language complexity. While the feature is designed to be minimal and consistent with existing `for` loop semantics, it adds another way to express iteration and table construction, which may impact readability for developers unfamiliar with the syntax. From 7a83f2f08733f71e76034c1201717286345431d4 Mon Sep 17 00:00:00 2001 From: InfraredGod <150057615+InfraredGodYT@users.noreply.github.com> Date: Mon, 30 Mar 2026 17:54:19 -0500 Subject: [PATCH 8/8] Update table-comprehension.md Removing the use of ipairs in examples --- docs/table-comprehension.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/table-comprehension.md b/docs/table-comprehension.md index a8dc0171..e9c83ba9 100644 --- a/docs/table-comprehension.md +++ b/docs/table-comprehension.md @@ -13,12 +13,12 @@ Examples: -- Arrays local numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} local doubled = {} -for i, v in ipairs(numbers) do +for i, v in numbers do doubled[i] = v * 2 end local evens = {} -for _, v in ipairs(numbers) do +for _, v in numbers do if v % 2 == 0 then table.insert(evens, v) end @@ -26,12 +26,12 @@ end -- Maps local playersByName = {} -for _, player in ipairs(game.Players:GetPlayers()) do +for _, player in game.Players:GetPlayers() do playersByName[player.Name] = player end local alivePlayers = {} -for _, player in ipairs(game.Players:GetPlayers()) do +for _, player in game.Players:GetPlayers() do if player.Health > 0 then alivePlayers[player.UserId] = player end @@ -62,8 +62,8 @@ Two forms of table comprehension are proposed. Examples: ```lua -local doubled = {v * 2 for i, v in ipairs(numbers)} -local evens = {v for _, v in ipairs(numbers) if v % 2 == 0} +local doubled = {v * 2 for i, v in numbers} +local evens = {v for _, v in numbers if v % 2 == 0} ``` #### Map-style @@ -74,11 +74,11 @@ Examples: ```lua local playersByName = { [player.Name] = player - for _, player in ipairs(game.Players:GetPlayers()) + for _, player in game.Players:GetPlayers() } local alivePlayers = { [player.UserId] = player - for _, player in ipairs(game.Players:GetPlayers()) + for _, player in game.Players:GetPlayers() if player.Health > 0 } ``` @@ -188,7 +188,7 @@ Continue using `for` loops to construct tables: ```lua local result = {} -for _, v in ipairs(items) do +for _, v in items do if v > 1 then table.insert(result, v * 2) end @@ -204,7 +204,7 @@ Users may define utility functions for common patterns such as mapping or filter ```lua local function map(t, fn) local result = {} - for i, v in ipairs(t) do + for i, v in t do result[i] = fn(v) end return result