Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Functions

Functions in Teal should work like you expect, and we have already showed various examples.

You can declare nominal function types, like we do for records, to avoid long-winded type declarations, especially when declaring functions that take callbacks. This is done with using function types, and they can be generic as well:

local type Comparator = function<T>(T, T): boolean

local function mysort<A>(arr: {A}, cmp?: Comparator<A>)
   -- ...
end

Note that functions can have optional arguments, as in the cmp? example above. This only affects the arity of the functions (that is, the number of arguments passed to a function), not their types. Note that the question mark is assigned to the argument name, not its type. If an argument is not optional, it may still be given explicitly as nil.

Another thing to know about function declarations is that you can parenthesize the declaration of return types, to avoid ambiguities when using nested declarations and multiple returns:

f: function(function(? string):(number, number), number)

Note also that in this example the string argument of the return function type is optional. When declaring optional arguments in function type declarations which do not use argument names, The question mark is placed ahead of the type. Again, this is an attribute of the argument position, not of the argument type itself.

You can declare functions that generate iterators which can be used in for statements: the function needs to produce another function that iterates. This is an example taken the book “Programming in Lua”:

local function allwords(): (function(): string)
   local line = io.read()
   local pos = 1
   return function(): string
      while line do
         local s, e = line:find("%w+", pos)
         if s then
            pos = e + 1
            return line:sub(s, e)
         else
            line = io.read()
            pos = 1
         end
      end
      return nil
   end
end

for word in allwords() do
   print(word)
end

The only changes made to the code above were the addition of type signatures in both function declarations.

Teal also supports macro expressions, which are a restricted form of function whose contents are expanded inline when generating Lua code.

Function declaration syntax

Unlike Lua, bare function declarations are not global by default. Like other variables, Teal requires functions to be declared as local or global.

local function a_local_function()
end

global function a_global_function()
end

One exception to the need of a local or global discriminator is when using the “record function syntax”, which is used to declare and assign a function element to a record, because the visibility is already defined by the record itself. You can use this syntax within the same scope where a record is declared:

local record MyRecord
end

function MyRecord.a_record_function()
end

That supports both . and : notation, for an implied first argument self:

local record MyRecord
   x: integer
end

function MyRecord:print_x()
   print(self.x)
end

Note that this syntax is only supported for records. It cannot be used with interfaces, because they are abstract. It also cannot be used with maps, emphasizing that records and maps are distinct types with distinct uses in Teal. Even though both are implemented as Lua tables, they are not interchangeable. If you have a map with function values, you can use the assignment syntax. As a matter of style, you may want to use the array index syntax, to emphasize to readers of your code that this is a map, as the declaration and assignment might be far apart in the source:

local funcs = {string : function(string):(integer)}

funcs["count_a"] = function(input: string)
   local n = 0
   for _ in input:gmatch("a") do
      n = n + 1
   end
   return n
end

The example above also showcases using a function as a value – in this case, being assigned to a map. Like in Lua, you can pass functions as argument to functions, assign them to variables, maps, record fields, and so on, as long as their types as compatible.

Variadic functions

Just like in Lua, some functions in Teal may receive a variable amount of arguments. Variadic functions can be declared by specifying ... as the last argument of the function:

local function test(...: number)
   print(...)
end

test(1, 2, 3)

In case your function returns a variable amount of values, you may also declare variadic return types by using the type... syntax:

local function test(...: number): number...
   return ...
end

local a, b, c = test(1, 2, 3)

If your function is very dynamic by nature (for example, you are typing a Lua function that can return anything), a typical return type will be any.... When using these functions, often one knows at the call site what are the types of the expected returns, given the arguments that were passed. To set the types of these dynamic returns, you can use the as operator over multiple values, using a parenthesized list of types:

local s = { 1234, "ola" }
local a, b = table.unpack(s) as (number, string)

print(a + 1)      -- `a` has type number
print(b:upper())  -- `b` has type string