View all results

User-defined functions

A user-defined function is a reusable piece of logic declared within a script. Once declared, the function can be called like any built-in function.

example.fql Ferret v2
query.fql
FQL
FUNC double(x) => x * 2 RETURN double(21)

Declaration

A function declaration begins with the FUNC keyword, followed by a name, a parameter list in parentheses, and a body.

There are two body forms: arrow and block.

Arrow form

The arrow form uses => followed by a single expression. The result of the expression is the return value.

example.fql Ferret v2
query.fql
FQL
FUNC greet(name) => CONCAT("Hello, ", name, "!") RETURN greet("Ada")

Use the arrow form when the function body is a single expression.

Block form

Unlike many C-like languages, FQL uses parentheses for block function bodies.

example.fql Ferret v2
query.fql
FQL
FUNC normalizePrice(input) ( LET cleaned = TRIM(input) LET numeric = SUBSTITUTE(cleaned, "$", "") RETURN TO_FLOAT(numeric) ) RETURN normalizePrice(" $19.99 ")

Use the block form when the function body requires intermediate variables or multiple steps.

Parameters

Parameters are listed inside parentheses, separated by commas.

example.fql Ferret v2
query.fql
FQL
FUNC fullName(first, last) => CONCAT(first, " ", last) RETURN fullName("Ada", "Lovelace")

A function may have no parameters:

example.fql Ferret v2
query.fql
FQL
FUNC now() => "2024-01-01" RETURN now()

Parameters are positional. The caller must provide exactly the number of arguments the function expects.

Capturing outer variables

A function body can read variables from the enclosing scope.

example.fql Ferret v2
query.fql
FQL
LET base = 10 FUNC add(value) => base + value RETURN add(5)

If the outer variable is declared with VAR, the function can also modify it:

example.fql Ferret v2
query.fql
FQL
VAR counter = 0 FUNC inc() ( counter = counter + 1 RETURN counter ) RETURN [inc(), inc(), inc()]

Variables declared with LET are immutable and cannot be reassigned inside a function.

Nesting functions

Functions can be declared inside other functions.

example.fql Ferret v2
query.fql
FQL
FUNC process(items) ( FUNC transform(item) => item * 2 RETURN ( FOR item IN items RETURN transform(item) ) ) RETURN process([1, 2, 3])

A nested function can access variables from all enclosing scopes, not just the immediately surrounding one.

Using functions in loops

User-defined functions work naturally with FOR loops and other query constructs.

example.fql Ferret v2
query.fql
FQL
FUNC formatUser(user) ( RETURN { label: CONCAT(user.name, " (", user.role, ")"), active: user.active } ) LET users = [ { name: "Ada", role: "admin", active: true }, { name: "Grace", role: "editor", active: false }, { name: "Linus", role: "viewer", active: true } ] FOR user IN users FILTER user.active RETURN formatUser(user)

Function names

Function names follow the same rules as variable names: they must start with a letter or underscore, followed by any combination of letters, digits, and underscores.

Function names are case-sensitive. add and Add are different functions.

example.fql Ferret v2
query.fql
FQL
FUNC a() => 1 FUNC A() => 2 RETURN a() + A()

By convention, built-in functions are written in uppercase, while user-defined function names may use the style preferred by the script author.