mirror of
https://github.com/luau-lang/luau.git
synced 2024-11-15 14:25:44 +08:00
Luau Recap: October 2021 (#92)
* Draft for October post * Rebuild pages * Apply suggestions from code review Co-authored-by: dcope-rbx <91100513+dcope-rbx@users.noreply.github.com> * Update 2021-10-31-luau-recap-october-2021.md Add optimizations * Remove unfinished section Co-authored-by: dcope-rbx <91100513+dcope-rbx@users.noreply.github.com> Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
This commit is contained in:
parent
c1654e34eb
commit
e562596bb8
151
docs/_posts/2021-10-31-luau-recap-october-2021.md
Normal file
151
docs/_posts/2021-10-31-luau-recap-october-2021.md
Normal file
@ -0,0 +1,151 @@
|
||||
---
|
||||
layout: single
|
||||
title: "Luau Recap: October 2021"
|
||||
---
|
||||
|
||||
Luau is our new language that you can read more about at [https://luau-lang.org](https://luau-lang.org).
|
||||
|
||||
[Cross-posted to the [Roblox Developer Forum](https://devforum.roblox.com/t/luau-recap-october-2021/).]
|
||||
|
||||
## if-then-else expression
|
||||
|
||||
In addition to supporting standard if *statements*, Luau adds support for if *expressions*.
|
||||
Syntactically, `if-then-else` expressions look very similar to if statements.
|
||||
However instead of conditionally executing blocks of code, if expressions conditionally evaluate expressions and return the value produced as a result.
|
||||
Also, unlike if statements, if expressions do not terminate with the `end` keyword.
|
||||
|
||||
Here is a simple example of an `if-then-else` expression:
|
||||
```lua
|
||||
local maxValue = if a > b then a else b
|
||||
```
|
||||
|
||||
`if-then-else` expressions may occur in any place a regular expression is used.
|
||||
The `if-then-else` expression must match `if <expr> then <expr> else <expr>`;
|
||||
it can also contain an arbitrary number of `elseif` clauses, like `if <expr> then <expr> elseif <expr> then <expr> else <expr>`.
|
||||
Note that in either case, `else` is mandatory.
|
||||
|
||||
Here's is an example demonstrating `elseif`:
|
||||
```lua
|
||||
local sign = if x < 0 then -1 elseif x > 0 then 1 else 0
|
||||
```
|
||||
|
||||
**Note:** In Luau, the `if-then-else` expression is preferred vs the standard Lua idiom of writing `a and b or c` (which roughly simulates a ternary operator). However, the Lua idiom may return an unexpected result if `b` evaluates to false.
|
||||
The `if-then-else` expression will behave as expected in all situations.
|
||||
|
||||
## Library improvements
|
||||
|
||||
New additions to the `table` library have arrived:
|
||||
|
||||
```lua
|
||||
function table.freeze(t)
|
||||
```
|
||||
|
||||
Given a non-frozen table, freezes it such that all subsequent attempts to modify the table or assign its metatable raise an error.
|
||||
If the input table is already frozen or has a protected metatable, the function raises an error; otherwise it returns the input table.
|
||||
Note that the table is frozen in-place and is not being copied.
|
||||
Additionally, only `t` is frozen, and keys/values/metatable of `t` don't change their state and need to be frozen separately if desired.
|
||||
|
||||
```lua
|
||||
function table.isfrozen(t): boolean
|
||||
```
|
||||
|
||||
Returns `true` if and only if the input table is frozen.
|
||||
|
||||
## Typechecking improvements
|
||||
|
||||
We continue work on our type constraint resolver and have multiple improvements this month.
|
||||
|
||||
We now resolve constraints that are created by `or` expressions.
|
||||
In the following example, by checking against multiple type alternatives, we learn that value is a union of those types:
|
||||
```lua
|
||||
--!strict
|
||||
local function f(x: any)
|
||||
if type(x) == "number" or type(x) == "string" then
|
||||
local foo = x -- 'foo' type is known to be 'number | string' here
|
||||
-- ...
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Support for `or` constraints allowed us to handle additional scenarios with `and` and `not` expressions to reduce false positives after specific type guards.
|
||||
|
||||
And speaking of type guards, we now correctly handle sub-class relationships in those checks:
|
||||
```lua
|
||||
--!strict
|
||||
local function f(x: Part | Folder | string)
|
||||
if typeof(x) == "Instance" then
|
||||
local foo = x -- 'foo' type is known to be 'Part | Folder' here
|
||||
else
|
||||
local bar = x -- 'bar' type is known to be 'string' here
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
One more fix handles the `a and b or c` expression when 'b' depends on 'a':
|
||||
```lua
|
||||
--!strict
|
||||
function f(t: {x: number}?)
|
||||
local a = t and t.x or 5 -- 'a' is a 'number', no false positive errors here
|
||||
end
|
||||
```
|
||||
|
||||
Of course, our new if-then-else expressions handle this case as well.
|
||||
```lua
|
||||
--!strict
|
||||
function f(t: {x: number}?)
|
||||
local a = if t then t.x else 5 -- 'a' is a 'number', no false positive errors here
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
We have extended bidirectional typechecking that was announced last month to propagate types in additional statements and expressions.
|
||||
```lua
|
||||
--!strict
|
||||
function getSortFunction(): (number, number) -> boolean
|
||||
return function(a, b) return a > b end -- a and b are now known to be 'number' here
|
||||
end
|
||||
|
||||
local comp = getSortFunction()
|
||||
|
||||
comp = function(a, b) return a < b end -- a and b are now known to be 'number' here as well
|
||||
```
|
||||
|
||||
---
|
||||
We've also improved some of our messages with union types and optional types (unions types with `nil`).
|
||||
|
||||
When optional types are used incorrectly, you get better messages. For example:
|
||||
```lua
|
||||
--!strict
|
||||
function f(a: {number}?)
|
||||
return a[1] -- "Value of type '{number}?' could be nil" instead of "'{number}?' is not a table'
|
||||
end
|
||||
```
|
||||
|
||||
When a property of a union type is accessed, but is missing from some of the options, we will report which options are not valid:
|
||||
```lua
|
||||
--!strict
|
||||
type A = { x: number, y: number }
|
||||
type B = { x: number }
|
||||
local a: A | B
|
||||
local b = a.y -- Key 'y' is missing from 'B' in the type 'A | B'
|
||||
```
|
||||
|
||||
---
|
||||
When we enabled generic functions last month, some users might have seen a strange error about generic functions not being compatible with regular ones.
|
||||
|
||||
This was caused by undefined behaviour of recursive types.
|
||||
We have now added a restriction on how generic type parameters can be used in recursive types: [RFC: Recursive type restriction](https://github.com/Roblox/luau/blob/master/rfcs/recursive-type-restriction.md)
|
||||
|
||||
## Performance improvements
|
||||
|
||||
An improvement to the Stop-The-World (atomic in Lua terms) stage of the garbage collector was made to reduce time taken by that step by 4x factor.
|
||||
While this step only happens once during a GC cycle, it cannot be split into small parts and long times were visible as frame time spikes.
|
||||
|
||||
Table construction and resize was optimized further; as a result, many instances of table construction see 10-20% improvements
|
||||
for smaller tables on all platforms and 20%+ improvements on Windows.
|
||||
|
||||
Bytecode compiler has been optimized for giant table literals, resulting in 3x higher compilation throughput for certain files on AMD Zen architecture.
|
||||
|
||||
Coroutine resumption has been optimized and is now ~10% faster for coroutine-heavy code.
|
||||
|
||||
Array reads and writes are also now a bit faster resulting in 1-3% lift in array-heavy benchmarks.
|
Loading…
Reference in New Issue
Block a user