Luau Recap: March 2022

Luau is our new language that you can read more about at https://luau-lang.org.

[Cross-posted to the Roblox Developer Forum.]

Singleton types

We added support for singleton types! These allow you to use string or boolean literals in types. These types are only inhabited by the literal, for example if a variable x has type "foo", then x == "foo" is guaranteed to be true.

Singleton types are particularly useful when combined with union types, for example:

type Animals = "Dog" | "Cat" | "Bird"

or:

type Falsey = false | nil

In particular, singleton types play well with unions of tables, allowing tagged unions (also known as discriminated unions):

type Ok<T> = { type: "ok", value: T }
type Err<E> = { type: "error", error: E }
type Result<T, E> = Ok<T> | Err<E>

local result: Result<number, string> = ...
if result.type == "ok" then
    -- result :: Ok<number>
    print(result.value)
elseif result.type == "error" then
    -- result :: Err<string>
    error(result.error)
end

The RFC for singleton types is https://github.com/Roblox/luau/blob/master/rfcs/syntax-singleton-types.md

Width subtyping

A common idiom for programming with tables is to provide a public interface type, but to keep some of the concrete implementation private, for example:

type Interface = {
    name: string,
}

type Concrete = {
    name: string,
    id: number,
}

Within a module, a developer might use the concrete type, but export functions using the interface type:

local x: Concrete = {
    name = "foo",
    id = 123,
}

local function get(): Interface
    return x
end

Previously examples like this did not typecheck but now they do!

This language feature is called width subtyping (it allows tables to get wider, that is to have more properties).

The RFC for width subtyping is https://github.com/Roblox/luau/blob/master/rfcs/sealed-table-subtyping.md

Typechecking improvements

  • Generic function type inference now works the same for generic types and generic type packs.
  • We improved some error messages.
  • There are now fewer crashes (hopefully none!) due to mutating types inside the Luau typechecker.
  • We fixed a bug that could cause two incompatible copies of the same class to be created.
  • Luau now copes better with cyclic metatable types (it gives a type error rather than hanging).
  • Fixed a case where types are not properly bound to all of the subtype when the subtype is a union.
  • We fixed a bug that confused union and intersection types of table properties.
  • Functions declared as function f(x : any) can now be called as f() without a type error.

API improvements

  • Implement table.clone which takes a table and returns a new table that has the same keys/values/metatable. The cloning is shallow - if some keys refer to tables that need to be cloned, that can be done manually by modifying the resulting table.

Debugger improvements

  • Use the property name as the name of methods in the debugger.

Performance improvements

  • Optimize table rehashing (~15% faster dictionary table resize on average)
  • Improve performance of freeing tables (~5% lift on some GC benchmarks)
  • Improve gathering performance metrics for GC.
  • Reduce stack memory reallocation.

Updated: