Skip to content

Types

Every function in Rodash is dynamically and statically typed.

This means you know what arguments Rodash functions can take and what they can return when you write your code, and Rodash will also check that you've passed in valid values when you run it.

Dynamic Typing

Dynamic typing means checking that the values passed into a function are valid when the function is run. Rodash functions will throw a BadInput error if any arguments are invalid, allowing you to catch errors quickly and fail fast during development.

Rodash uses the "t" library by Osyris to perform runtime type assertions, which we recommend using in your own code during both development and production.

Static Typing

Lua is a dynamically-typed language, which means that you can't tell from normal Lua source code what type of values you should use when calling functions, unless you understand how the function internals work.

Rodash uses a type language that borrows heavily from the Typescript type language.

Types are added using optional annotations, which are added using --:. For example:

--: string, string -> bool
function endsWith(str, suffix)

This states that dash.endsWith takes two string arguments and returns a boolean.

Lua primitives

These types correspond to basic Lua types:

Name Type Description
Number number A Lua number
String string A Lua string
Boolean bool A Lua boolean
Nil nil The Lua nil
Userdata userdata A Lua userdata object
Table table A Lua table i.e. with type(value) == "table"

Extended primitives

Name Usage Description
Any any A type representing all of the valid types (excluding fail)
Some some A type representing all of the valid non-nil types
Character char A single character of a string
Pattern pattern A string representing a valid Lua pattern
Integer int An integer
Unsigned integer uint An unsigned (positive) integer
Float float A floating point number
Never never A Promise that never resolves
Void void or () An empty tuple, typically to represent an empty return type or empty function-arguments
Fail fail A return value which means that the function will always throw an error

Structural types

Like many scripting languages, Lua has a general structure type table which can be used to represent arrays, dictionaries, sets, classes, and many other data types. The type language uses a strict set of definitions for different ways tables can be used:

Name Usage Description
Tuple (X, Y, Z) Values of particular types separated by commas, such as an argument or return list in a Lua function
Array X[] A Lua table that has a finite number of Ordered keys with values of type X
Fixed array {X, Y, Z} An array with a fixed number of values, where the first value has type X, the second type Y etc.
Table {} a Lua table with no specific keys or values
Dictionary X{} a Lua table which has values of type X
Map {[X]: Y} A Lua table with keys of type X mapping to values of type Y
Multi-dimensional table X[][]
X{}{}
X{}[]{}
A 2d or higher dimensional table, with values being arrays, dictionaries or multi-dimensional tables themselves

Rodash methods use more general types where possible, specifically Ordered and Iterable values. This let's you operate on iterators as well as tables.

Function types

Name Type Description
Function X, Y -> Z A callable value taking parameters with type X and Y and returning a value of type Z
Multiple returns X -> Y, Z A callable value taking a parameter of type X and returning two values of type Y and Z respectively
Void function () -> () A function that takes no parameters and returns nothing
Rest function ...X -> ...Y A function that takes any number of parameters of type X and returns any number values of type Y

Modifying types

These types can be used in function signatures:

Name Usage Description
Mutable parameter mut X A function which takes a value of type mut X may modify the value of type X during execution
Yield return yield X A function which returns yield X may yield when executed before returning a value of type X
Self self X A function which takes self X as a parameter must be defined using : and called as a method on a value of type X. Only required if the method should be called on a different type to the one it is defined under

Callable

Any value that can be called has a function type. In practice, a value is callable if and only if it is one of the following:

  • A Lua function
  • A Lua CFunction
  • A Lua table with a metatable that has a valid __call property defined

This is more general than just checking if a value has a function type.

Usage

You can test if a value is callable using dash.isCallable.

Composite types

Name Type Description
Optional X? Equivalent to the type X | nil, meaning a value of this type either has type X or nil
Union X | Y Values of this type can either be of type X or of type Y (or both)
Intersection X & Y Values of this type must be of both types X and Y. For example, if X and Y are interfaces the value must implement both of them.

Type definitions

There a few different ways to define new types in documented code:

Name Usage Description
Marker type type X = Y This creates a type called X that you can refer to which is the same as the type Y. For example, a Constructor is just a specific type function, but using Constructor also indicates how the function should be used beyond its type
Interface interface X This creates a type called X based on functions in a Lua table and a t interface
Class class X This creates a type X based on methods in a Lua table and fields in a t interface, or use dash.classWithInterface
Enum enum X This creates a type X based on keys of a Lua array, or use dash.enum
Symbol symbol X This creates a type X based on a Lua table definition, or use dash.symbol

Generic types

Generics allow the same function to be used with different types. Known types are replaced with type variables, which are usually single capital letters such as T or K, but can be any word beginning with a capital letter.

When a generic function is called, the same type variable must refer to the same type everywhere it appears in the type definition.

Name Type Description
Generic function <T>(T -> T) A generic function that takes a parameter of type T and returns a value of the same type T
Generic arguments <A>(...A -> A) A generic function that takes any number of parameters of type A and returns a value of the same type A
Generic bounds <T: X>(T -> T) A function that has a type variable T which must satisfy the type X
Parameterized object X<T> An object of type X that is parameterized to type T

Examples

The dash.last function returns the last element of an array, and has a type signature of <T>(T[] -> T). If you call it with an array of strings with type string[], then T = string and the function becomes parameterized, meaning its type is string[] -> string. This shows you that the function will return a string. If you simply used any[] -> any without using a generic type, you couldn't know that the function always returned a value of the same type.

Like T[] was parameterized as a string array when T = string, any structural types like dictionaries or Classes can also parameterized.

Usage

Note that using ...X when X refers to a tuple expands the elements from the tuple in-order, as a value can't have a tuple type itself. For example, dash.id has the type signature <A>(...A -> ...A). If you call id(1, "Hello") then A = (number, string) and the function becomes typed as: number, string -> number, string.

Iterable types

Iterators

An iterator is a function which returns (key, value) pairs when it is called. You might not use them often in your own code but they are very common in Lua - any loop you write takes an iterator. For example ipairs(object) in the code:

for key, value in ipairs(object) do

They are more abstract than using a concrete table to store data which means you can use them to:

  • Represent an infinite list, such as countable sequences of numbers like the naturals.
  • Represent a stream, such as a random stream or events coming from an input source.
  • Avoid calculating all the elements of a list at once, such as a lazy list retrieving elements from an HTTP endpoint

You cannot modify the source of values in an iterator, so they are safer to use if you don't want a function changing the underlying source.

Type

type Iterator<K,V> = (props: any, previousKey: K?) -> K, V

Generics

K - some - the primary key type (can be any non-nil value)

V - some - the secondary key type (can be any non-nil value)

Stateful Iterator

function statefulIterator() --> K, V

Stateful iterators take no arguments and simply return the next (key, value) pair when called. These are simple to make and prevent code from skipping or seeking to arbitrary elements in the underlying source.

You can use dash.iterator to create your own iterator for a table. This is useful where you cannot use pairs or ipairs natively such as when using read-only objects - see dash.freeze.

Type

<K,V>(() -> K, V)

Generics

K - some - the primary key type (can be any non-nil value)

V - some - the secondary key type (can be any non-nil value)

Examples

-- Calling range returns a stateful iterator that counts from a to b.
function range(a, b)
    local key = 0
    return function()
        local value = a + key
        key = key + 1
        if value <= b then
            return key, value
        end
    end
end

Usage

Stateful iterators that you write can be used in any Rodash function that takes an Iterable.

Stateless Iterator

function statelessIterator(props, previousKey) --> K, V

Stateless iterators take two arguments (props, previousKey) which are used to address a (key, value) pair to return.

A stateless iterator should return the first (key, value) pair if the previousKey nil is passed in.

Type

<K,V>((any, K?) -> K, V)

Properties

props - any - any value - the static properties that the iterator uses previousKey - K? - the iterator state type (optional) - (default = nil) the previous key acts as the state for the iterator so it doesn't need to store its own state

Generics

K - some - the primary key type (can be any non-nil value)

V - some - the secondary key type (can be any non-nil value)

S - some - the iterator state type (can be any non-nil value)

Examples

-- This function is a stateless iterator 
function evenNumbers(_, previousKey)
    local key = (previousKey or 0) + 1
    return key, key * 2
end

Usage

Stateless iterators that you write can be used in any Rodash function that takes an Iterable.

Iterable

An iterable value is either a dictionary or an Iterator. Many Rodash functions can operate on iterator functions as well as tables.

Type

type Iterable<K,V> = {[K]:V} | Iterator<K,V>

Generics

K - any - the primary key type (can be any value)

V - any - the primary value type (can be any value)

See

Ordered

An ordered value is either an array or an ordered Iterator. For something to be ordered, the keys returned during iteration must be the natural number sequence. This means the first key must be 1, the second 2, the third 3, etc.

Functions that operate on ordered values will ignore any additional keys, including numeric keys after a hole, which corresponds with how ipairs natively iterates through a table. For example, in the table {10, 20, [4] = 40, [5] = 50}, only the first two values are considered ordered.

Type

type Ordered<V> = V[] | Iterator<number,V>

Generics

V - any - the primary value type (can be any value)

Usage

For example, you could write an ordered iterator of numbers, and dash.first will to return the first number which matches a particular condition.

See

Asynchronous types

Promise

Any promise value created using the Roblox Lua Promise library has Promise<T> type. See the documentation of this library for examples on how to use promises.

Type

interface Promise<T>

Generics

T - any - the primary type (can be any value)

Yieldable

A marker type for a function which may yield. We recommend you use promises instead of writing your own yielding functions as they can have unpredictable behavior, such as causing threads to block outside your control.

Because of this, only functions which are marked as yieldable are assumed to be capable of yielding.

Type

type Yieldable<T> = ... -> yield T

Generics

T - any - the primary type (can be any value)

Async

A marker type for a function which returns a promise.

Type

type Async<T> = ... -> Promise<T>

Generics

T - any - the primary type (can be any value)

Class types

Class

Classes are the building block of object-oriented programming and are represented as tables in Lua. Every class instance has a metatable which points back to its class, allowing it to inherit class methods.

A class definition defines the unique type T. Any instance of the class satisfies the type T.

Type

interface Class<T:{}>

Generics

T - {} - the class instance type (can be a table)

Properties

Property Type Description
name string the name of the class
new(...) Constructor<T> returns a new instance of the class

See

Constructor

A marker type for functions which return a new instance of a particular class.

Type

type Constructor<T:{}> = ... -> T

Generics

T - {} - the class instance type (can be a table)

Enum

A marker type for an enumeration. An enumeration is a fixed dictionary of unique values that allow you to name different states that a value can take.

An enum definition defines the unique type T. Any value which is equal to a value in the enumeration satisfies the type T.

Type

type Enum<T:some> = {[key:string]:T}

Generics

T - some - the unique type of the enum (can be any non-nil value)

See

  • dash.enum - to create and use your own string enums.

Symbol

A marker type for a symbol.

An symbol definition defines the unique type T. Only the value of the symbol satisfies the type T.

Type

type Symbol<T:some>

Generics

T - some - the unique type of the symbol (can be any non-nil value)

See

Decorator types

Decorator

A marker type for decorator functions. A decorator is a function that takes a class and returns a modified version of the class which new behavior.

Type

type Decorator<T:{}> = Class<T> -> Class<T>

Generics

T - {} - the class instance type (can be a table)

Cloneable

An interface that lets implementors be cloned.

Type

interface Cloneable<T:{}>

Generics

T - {} - the class instance type (can be a table)

Properties

Property Type Description
clone() T returns a copy of the instance

See

  • dash.Cloneable - to derive a generated implementation of this interface.

Ord

An interface that means implementors of the same type T form a total order, similar to the Rust Ord trait.

Type

interface Ord<T:{}>

Generics

T - {} - the class instance type (can be a table)

Properties

Implementors can be compared using the ordering operators such as <, <=, and ==.

An ordering means that you can compare any elements a, b and c, and these properties are satisfied:

  • Two elements are always ordered i.e. exactly one of a < b, a == b or a > b is true
  • Transitive - if a < b and b < c then a < c

See

  • dash.Ord - to derive a generated implementation of this interface.

Eq

An interface that means implementors of the same type T form an equivalence relation, similar to the Rust Eq trait.

Type

interface Eq<T:{}>

Generics

T - {} - the class instance type (can be a table)

Properties

Implementors can be compared using the equality operators such as == and ~=.

An equivalence relation means that you can compare any elements a, b and c, and these properties are satisfied:

  • Reflexive - a == a is always true
  • Symmetric - if a == b then b == a
  • Transitive - if a == b and b == c then a == c

See

  • dash.ShallowEq - to derive a generated implementation of this interface.

Chaining types

Chain

A chain is a function which can operate on a subject and return a value. They are built by composing a "chain" of functions together, which means that when the chain is called, each function runs on the subject in order, with the result of one function passed to the next.

A chain also provides methods based on the Chainable functions that the chain is created with. Each one takes the additional arguments of the chainable and returns a new chain that has the operation of the function called "queued" to the end of it.

Type

type Chain<S,T:Chainable<S>{}> = Chainable<S> & (... -> Chain<S,T>){}

Generics

S - any - the subject type (can be any value)

T - Chainable<S>{} - the chain's interface type (can be a dictionary (of Chainables (of the subject type)))

See

  • dash.chain - to make you own chains
  • dash.fn - to make a chain with Rodash functions

Chainable

A marker type for any function that is chainable, meaning it takes a subject as its first argument and "operates" on that subject in some way using any additional arguments.

Type

type Chainable<S> = S, ... -> S

Generics

S - any - the subject type (can be any value)

Actor

A marker type for a function that acts as an actor in a chain. An actor is a function that is called to determine how each function in the chain should evaluate its arguments and the subject value.

By default, a chain simply invokes the function with the subject and additional arguments.

Actors can be used to transform the types of subjects that functions can naturally deal with, without having to change the definition of the functions themselves.

For example the dash.maybe actor factory allows functions to be skipped if the subject is nil, and dash.continue allows functions to act on promises once they have resolved.

Type

type Actor<S> = (S -> S), S, ... -> S

See

Library types

Clearable

A stateful object with a clear method that resets the object state.

Type

interface Clearable<A>

Generics

A - any - the primary arguments

Properties

Property Type Description
clear(...) ...A -> () reset the object state addressed by the arguments provided

See

AllClearable

A stateful object with a clearAll method that resets the all parts of the object state.

Type interface AllClearable

Properties

Property Type Description
clearAll() () -> () reset the entire object state

See

DisplayString

A DisplayString is a string that is a valid argument to dash.formatValue. Examples include:

  • #b - prints a number in its binary representation
  • #? - pretty prints a table using dash.pretty

Usage

See dash.format for a full description of valid display strings.

SerializeOptions

Customize how dash.serialize, dash.serializeDeep and dash.pretty convert objects into strings using these options:

Type

interface SeralizeOptions<T: Iterable<K,V>>

Generics

K - any - the primary key type (can be any value)

V - any - the primary value type (can be any value)

T - Iterable<K,V> - An Iterable (of the primary key type and the primary value type)

Properties

Property Type Description
keys K[]? (optional) if defined, only the keys present in this array will be serialized
omitKeys K[]? (optional) an array of keys which should not be serialized
serializeValue(value) V -> string (default = dash.defaultSerializer) returns the string representation for a value in the object
serializeKey(key) K -> string (default = dash.defaultSerializer) returns the string representation for a key in the object
serializeElement(keyString, valueString, options) string, string, SerializeOptions<T> -> string returns the string representation for a serialized key-value pair
serializeTable(contents, ref, options) string[], string?, SerializeOptions<T> -> string (default = returns "{elements}") given an array of serialized table elements and an optional reference, this returns the string representation for a table
keyDelimiter ":" The string that is put between a serialized key and value pair
valueDelimiter "," The string that is put between each element of the object

BackoffOptions

Customize the function of dash.retryWithBackoff using these options:

Type

interface SeralizeOptions<T>

Generics

T - any - the primary type

Properties

Property Type Description
maxTries int how many tries (including the first one) the function should be called
retryExponentInSeconds number customize the backoff exponent
retryConstantInSeconds number customize the backoff constant
randomStream Random use a Roblox "Random" instance to control the backoff
shouldRetry(response) T -> bool called if maxTries > 1 to determine whether a retry should occur
onRetry(waitTime, errorMessage) (number, string) -> nil a hook for when a retry is triggered, with the delay before retry and error message which caused the failure
onDone(response, durationInSeconds) (T, number) -> nil a hook for when the promise resolves
onFail(errorMessage) string -> nil a hook for when the promise has failed and no more retries are allowed