From 3a173641df6432b0d94feb18bff89c517f56927b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petri=20H=C3=A4kkinen?= Date: Wed, 8 Feb 2023 20:18:33 +0200 Subject: [PATCH 1/6] RFC: Floor division operator --- rfcs/syntax-floor-division-operator.md | 61 ++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 rfcs/syntax-floor-division-operator.md diff --git a/rfcs/syntax-floor-division-operator.md b/rfcs/syntax-floor-division-operator.md new file mode 100644 index 000000000..752e5e52e --- /dev/null +++ b/rfcs/syntax-floor-division-operator.md @@ -0,0 +1,61 @@ +# Floor division operator + +## Summary + +Add floor division operator '//' to ease computing with integers. + +## Motivation + +Integers are everywhere. Indices, pixel coordinates, offsets, ranges, quantities, counters, rationals, fixed point arithmetic and bitwise operations all use integers. + +Luau is generally well suited to work with integers. The math operators +, -, \*, ^ and % support integers. That is, given integer operands these operators produce an integer result (provided that the result fits into representable range of integers). However, that is not the case with the division operator '/' which in the general case produces numbers with fractionals. + +To overcome this, typical Luau code performing integer computations needs to wrap the result of division inside a call to `math.floor`. This has a number of issues and can be error prone in practice. + +A typical mistake is to forget to use `math.floor`. This can produce subtle issues ranging from slightly wrong results to program crashes. A crash could occur, for example, when the result of division is used to fetch from a table with only integer keys, which produces nil and the program crashes soon after. Another type of crash occurs when an accidental fractional number is passed to a C function that expects integers. Particularly problematic is incorrect code which seems to work with frequently used data, only to fail with some rare input. For example, image sizes often have power of two dimensions, so code dealing with them may appear to work fine until much later some rare image has an odd size and a division by two in the code does not produce the correct result. Due to better ergonomics of the floor division operator, it becomes a second nature to write '//' everywhere when integers are involved and thus this class of bugs is much less likely to happen. + +Another issue with using `math.floor` as a workaround is that code performing a lot of integer calculations is harder to understand, write and maintain. + +Especially with applications dealing with pixel graphics, such as 2D games, integer math is so common that `math.floor` could easily become the most commonly used math library function. For these applications, avoiding the calls to `math.floor` is alluring from the performance perspective. + +> Non-normative: Here are the top math library functions used by a shipped game that heavily uses Lua: +> `floor`: 461 matches, `max`: 224 matches, `sin`: 197 matches, `min`: 195 matches, `clamp`: 171 matches, `cos`: 106 matches, `abs`: 85 matches. +> The majority of `math.floor` calls disappear from this codebase with the floor division operator. + +Finally, Lua has had floor division operator since version 5.3, so its addition to Luau makes it easier to migrate from Lua to Luau and perhaps more importantly use the wide variety of existing Lua libraries in Luau. + +## Design + +The design mirrors Lua 5.3: + +A new operator '//' will be added to the language. The operator performs division of two operands and rounds the result towards negative infinity. By default, the operator is only defined for numbers. The operator has the same precedence as the normal division operator ('/'). + +A new metamethod `__idiv` will be added. The metamethod is invoked when any operand of floor division is not a number. The metamethod can be used to implement floor division for any user defined data type as well as the built-in vector type. + + +The typechecker does not need special handling of the new operator. It can simply apply the same rules for floor division as it does for normal division operator. + +Examples of usage: + +``` +-- Convert offset into 2d indices +local i, j = offset % 5, offset // 5 + +-- Halve dimensions of an image or UI element +width, height = width // 2, height // 2 + +-- Draw a gui element to the center of the window +draw_gui_element(image, window_width // 2 - element_width // 2, window_height // 2 - element_height // 2) +``` + +## Drawbacks + +The addition of the new operator adds some complexity to the implementation (mostly to the VM) and to the language, which can be seen as a drawback. + +C like languages use '//' for line comments. Using the symbol '//' for floor division closes the door for using it for line comments in Luau. On the other hand, Luau already has long and short comment syntax, so adding yet another syntax for comments would add complexity to the language for little benefit. Moreover, it would make it harder to translate code from Lua to Luau and use existing Lua libraries if the symbol '//' has a completely different meaning in Lua and Luau. + +## Alternatives + +An alternative would be to do nothing but this would not solve the issues the lack of floor division currently has. + +An alternative implementation would treat '//' only as syntactic sugar. The addition of new VM opcode for floor division could be omitted and the compiler could be simply modified to automatically emit a call to `math.floor` when necessary. This would require only minimal changes to Luau, but it would not support overloading the floor division operator using metamethods and would not have the performance benefits of the full implementation. From 96d2cdb0c73713888d424353f391a956bee83973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petri=20H=C3=A4kkinen?= Date: Tue, 14 Feb 2023 08:58:41 +0200 Subject: [PATCH 2/6] Minor styling tweaks to floor division rfc --- rfcs/syntax-floor-division-operator.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/rfcs/syntax-floor-division-operator.md b/rfcs/syntax-floor-division-operator.md index 752e5e52e..656dbc758 100644 --- a/rfcs/syntax-floor-division-operator.md +++ b/rfcs/syntax-floor-division-operator.md @@ -2,17 +2,17 @@ ## Summary -Add floor division operator '//' to ease computing with integers. +Add floor division operator `//` to ease computing with integers. ## Motivation Integers are everywhere. Indices, pixel coordinates, offsets, ranges, quantities, counters, rationals, fixed point arithmetic and bitwise operations all use integers. -Luau is generally well suited to work with integers. The math operators +, -, \*, ^ and % support integers. That is, given integer operands these operators produce an integer result (provided that the result fits into representable range of integers). However, that is not the case with the division operator '/' which in the general case produces numbers with fractionals. +Luau is generally well suited to work with integers. The math operators +, -, \*, ^ and % support integers. That is, given integer operands these operators produce an integer result (provided that the result fits into representable range of integers). However, that is not the case with the division operator `/` which in the general case produces numbers with fractionals. To overcome this, typical Luau code performing integer computations needs to wrap the result of division inside a call to `math.floor`. This has a number of issues and can be error prone in practice. -A typical mistake is to forget to use `math.floor`. This can produce subtle issues ranging from slightly wrong results to program crashes. A crash could occur, for example, when the result of division is used to fetch from a table with only integer keys, which produces nil and the program crashes soon after. Another type of crash occurs when an accidental fractional number is passed to a C function that expects integers. Particularly problematic is incorrect code which seems to work with frequently used data, only to fail with some rare input. For example, image sizes often have power of two dimensions, so code dealing with them may appear to work fine until much later some rare image has an odd size and a division by two in the code does not produce the correct result. Due to better ergonomics of the floor division operator, it becomes a second nature to write '//' everywhere when integers are involved and thus this class of bugs is much less likely to happen. +A typical mistake is to forget to use `math.floor`. This can produce subtle issues ranging from slightly wrong results to program crashes. A crash could occur, for example, when the result of division is used to fetch from a table with only integer keys, which produces nil and the program crashes soon after. Another type of crash occurs when an accidental fractional number is passed to a C function that expects integers. Particularly problematic is incorrect code which seems to work with frequently used data, only to fail with some rare input. For example, image sizes often have power of two dimensions, so code dealing with them may appear to work fine until much later some rare image has an odd size and a division by two in the code does not produce the correct result. Due to better ergonomics of the floor division operator, it becomes a second nature to write `//` everywhere when integers are involved and thus this class of bugs is much less likely to happen. Another issue with using `math.floor` as a workaround is that code performing a lot of integer calculations is harder to understand, write and maintain. @@ -28,7 +28,7 @@ Finally, Lua has had floor division operator since version 5.3, so its addition The design mirrors Lua 5.3: -A new operator '//' will be added to the language. The operator performs division of two operands and rounds the result towards negative infinity. By default, the operator is only defined for numbers. The operator has the same precedence as the normal division operator ('/'). +A new operator `//` will be added to the language. The operator performs division of two operands and rounds the result towards negative infinity. By default, the operator is only defined for numbers. The operator has the same precedence as the normal division operator `/`. A new metamethod `__idiv` will be added. The metamethod is invoked when any operand of floor division is not a number. The metamethod can be used to implement floor division for any user defined data type as well as the built-in vector type. @@ -44,18 +44,18 @@ local i, j = offset % 5, offset // 5 -- Halve dimensions of an image or UI element width, height = width // 2, height // 2 --- Draw a gui element to the center of the window -draw_gui_element(image, window_width // 2 - element_width // 2, window_height // 2 - element_height // 2) +-- Draw an image to the center of the window +draw_image(image, window_width // 2 - element_width // 2, window_height // 2 - element_height // 2) ``` ## Drawbacks The addition of the new operator adds some complexity to the implementation (mostly to the VM) and to the language, which can be seen as a drawback. -C like languages use '//' for line comments. Using the symbol '//' for floor division closes the door for using it for line comments in Luau. On the other hand, Luau already has long and short comment syntax, so adding yet another syntax for comments would add complexity to the language for little benefit. Moreover, it would make it harder to translate code from Lua to Luau and use existing Lua libraries if the symbol '//' has a completely different meaning in Lua and Luau. +C like languages use `//` for line comments. Using the symbol `//` for floor division closes the door for using it for line comments in Luau. On the other hand, Luau already has long and short comment syntax, so adding yet another syntax for comments would add complexity to the language for little benefit. Moreover, it would make it harder to translate code from Lua to Luau and use existing Lua libraries if the symbol `//` has a completely different meaning in Lua and Luau. ## Alternatives An alternative would be to do nothing but this would not solve the issues the lack of floor division currently has. -An alternative implementation would treat '//' only as syntactic sugar. The addition of new VM opcode for floor division could be omitted and the compiler could be simply modified to automatically emit a call to `math.floor` when necessary. This would require only minimal changes to Luau, but it would not support overloading the floor division operator using metamethods and would not have the performance benefits of the full implementation. +An alternative implementation would treat `//` only as syntactic sugar. The addition of new VM opcode for floor division could be omitted and the compiler could be simply modified to automatically emit a call to `math.floor` when necessary. This would require only minimal changes to Luau, but it would not support overloading the floor division operator using metamethods and would not have the performance benefits of the full implementation. From fc9b14ce6959791bf2f103a0c59234aa537b3864 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petri=20H=C3=A4kkinen?= Date: Tue, 14 Feb 2023 09:12:35 +0200 Subject: [PATCH 3/6] Add //= to floor division rfc --- rfcs/syntax-floor-division-operator.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/rfcs/syntax-floor-division-operator.md b/rfcs/syntax-floor-division-operator.md index 656dbc758..fbe62c91d 100644 --- a/rfcs/syntax-floor-division-operator.md +++ b/rfcs/syntax-floor-division-operator.md @@ -28,12 +28,11 @@ Finally, Lua has had floor division operator since version 5.3, so its addition The design mirrors Lua 5.3: -A new operator `//` will be added to the language. The operator performs division of two operands and rounds the result towards negative infinity. By default, the operator is only defined for numbers. The operator has the same precedence as the normal division operator `/`. +New operators `//` and `//=` will be added to the language. The operator `//` performs division of two operands and rounds the result towards negative infinity. By default, the operator is only defined for numbers. The operator has the same precedence as the normal division operator `/`. `//=` is the compound-assignment operator for floor division, similar to the existing operator `/=`. A new metamethod `__idiv` will be added. The metamethod is invoked when any operand of floor division is not a number. The metamethod can be used to implement floor division for any user defined data type as well as the built-in vector type. - -The typechecker does not need special handling of the new operator. It can simply apply the same rules for floor division as it does for normal division operator. +The typechecker does not need special handling for the new operators. It can simply apply the same rules for floor division as it does for normal division operators. Examples of usage: @@ -58,4 +57,4 @@ C like languages use `//` for line comments. Using the symbol `//` for floor div An alternative would be to do nothing but this would not solve the issues the lack of floor division currently has. -An alternative implementation would treat `//` only as syntactic sugar. The addition of new VM opcode for floor division could be omitted and the compiler could be simply modified to automatically emit a call to `math.floor` when necessary. This would require only minimal changes to Luau, but it would not support overloading the floor division operator using metamethods and would not have the performance benefits of the full implementation. +An alternative implementation would treat `//` and `//=` only as syntactic sugar. The addition of new VM opcode for floor division could be omitted and the compiler could be simply modified to automatically emit a call to `math.floor` when necessary. This would require only minimal changes to Luau, but it would not support overloading the floor division operator using metamethods and would not have the performance benefits of the full implementation. From 483b4eb5da5c8ce868c651e5f32751450f2620dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petri=20H=C3=A4kkinen?= Date: Wed, 15 Feb 2023 13:27:45 +0200 Subject: [PATCH 4/6] Clarification: reworded a paragraph based on comments from zeux --- rfcs/syntax-floor-division-operator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/syntax-floor-division-operator.md b/rfcs/syntax-floor-division-operator.md index fbe62c91d..85f674809 100644 --- a/rfcs/syntax-floor-division-operator.md +++ b/rfcs/syntax-floor-division-operator.md @@ -12,7 +12,7 @@ Luau is generally well suited to work with integers. The math operators +, -, \* To overcome this, typical Luau code performing integer computations needs to wrap the result of division inside a call to `math.floor`. This has a number of issues and can be error prone in practice. -A typical mistake is to forget to use `math.floor`. This can produce subtle issues ranging from slightly wrong results to program crashes. A crash could occur, for example, when the result of division is used to fetch from a table with only integer keys, which produces nil and the program crashes soon after. Another type of crash occurs when an accidental fractional number is passed to a C function that expects integers. Particularly problematic is incorrect code which seems to work with frequently used data, only to fail with some rare input. For example, image sizes often have power of two dimensions, so code dealing with them may appear to work fine until much later some rare image has an odd size and a division by two in the code does not produce the correct result. Due to better ergonomics of the floor division operator, it becomes a second nature to write `//` everywhere when integers are involved and thus this class of bugs is much less likely to happen. +A typical mistake is to forget to use `math.floor`. This can produce subtle issues ranging from slightly wrong results to script errors. A script error could occur, for example, when the result of division is used to fetch from a table with only integer keys, which produces nil and a script error happens soon after. Another type of error occurs when an accidental fractional number is passed to a C function. Depending on the implementation, the C function could raise an error (if it checks that the number is actually an integer) or cause logic errors due to rounding. Particularly problematic is incorrect code which seems to work with frequently used data, only to fail with some rare input. For example, image sizes often have power of two dimensions, so code dealing with them may appear to work fine until much later some rare image has an odd size and a division by two in the code does not produce the correct result. Due to better ergonomics of the floor division operator, it becomes a second nature to write `//` everywhere when integers are involved and thus this class of bugs is much less likely to happen. Another issue with using `math.floor` as a workaround is that code performing a lot of integer calculations is harder to understand, write and maintain. From e55a2786278bc41cd6afd102f2e63a55724696d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petri=20H=C3=A4kkinen?= Date: Wed, 15 Feb 2023 13:34:27 +0200 Subject: [PATCH 5/6] Split long paragraph --- rfcs/syntax-floor-division-operator.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rfcs/syntax-floor-division-operator.md b/rfcs/syntax-floor-division-operator.md index 85f674809..40d125937 100644 --- a/rfcs/syntax-floor-division-operator.md +++ b/rfcs/syntax-floor-division-operator.md @@ -12,7 +12,9 @@ Luau is generally well suited to work with integers. The math operators +, -, \* To overcome this, typical Luau code performing integer computations needs to wrap the result of division inside a call to `math.floor`. This has a number of issues and can be error prone in practice. -A typical mistake is to forget to use `math.floor`. This can produce subtle issues ranging from slightly wrong results to script errors. A script error could occur, for example, when the result of division is used to fetch from a table with only integer keys, which produces nil and a script error happens soon after. Another type of error occurs when an accidental fractional number is passed to a C function. Depending on the implementation, the C function could raise an error (if it checks that the number is actually an integer) or cause logic errors due to rounding. Particularly problematic is incorrect code which seems to work with frequently used data, only to fail with some rare input. For example, image sizes often have power of two dimensions, so code dealing with them may appear to work fine until much later some rare image has an odd size and a division by two in the code does not produce the correct result. Due to better ergonomics of the floor division operator, it becomes a second nature to write `//` everywhere when integers are involved and thus this class of bugs is much less likely to happen. +A typical mistake is to forget to use `math.floor`. This can produce subtle issues ranging from slightly wrong results to script errors. A script error could occur, for example, when the result of division is used to fetch from a table with only integer keys, which produces nil and a script error happens soon after. Another type of error occurs when an accidental fractional number is passed to a C function. Depending on the implementation, the C function could raise an error (if it checks that the number is actually an integer) or cause logic errors due to rounding. + +Particularly problematic is incorrect code which seems to work with frequently used data, only to fail with some rare input. For example, image sizes often have power of two dimensions, so code dealing with them may appear to work fine until much later some rare image has an odd size and a division by two in the code does not produce the correct result. Due to better ergonomics of the floor division operator, it becomes a second nature to write `//` everywhere when integers are involved and thus this class of bugs is much less likely to happen. Another issue with using `math.floor` as a workaround is that code performing a lot of integer calculations is harder to understand, write and maintain. From b079d89c4a59b41b37a5524ecded0b41655b3be4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petri=20H=C3=A4kkinen?= Date: Sun, 19 Mar 2023 21:12:18 +0200 Subject: [PATCH 6/6] Add Python, R and Julia as examples of other languages having floor division operator --- rfcs/syntax-floor-division-operator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/syntax-floor-division-operator.md b/rfcs/syntax-floor-division-operator.md index 40d125937..8ec3913f0 100644 --- a/rfcs/syntax-floor-division-operator.md +++ b/rfcs/syntax-floor-division-operator.md @@ -24,7 +24,7 @@ Especially with applications dealing with pixel graphics, such as 2D games, inte > `floor`: 461 matches, `max`: 224 matches, `sin`: 197 matches, `min`: 195 matches, `clamp`: 171 matches, `cos`: 106 matches, `abs`: 85 matches. > The majority of `math.floor` calls disappear from this codebase with the floor division operator. -Finally, Lua has had floor division operator since version 5.3, so its addition to Luau makes it easier to migrate from Lua to Luau and perhaps more importantly use the wide variety of existing Lua libraries in Luau. +Lua has had floor division operator since version 5.3, so its addition to Luau makes it easier to migrate from Lua to Luau and perhaps more importantly use the wide variety of existing Lua libraries in Luau. Of other languages, most notably Python has floor division operator with same semantics and same syntax. R and Julia also have a similar operator. ## Design