Skip to main content

Functions

Functions in the Alia language correspond to alias definitions. Such a definition both registers a syntax pattern and declares an Alias object.

1. Function Definition​

alias supports several definition forms: alias <Keys pattern> -> <Stack implementation> defines a syntax keyword and its implementation; alias <Keys pattern> -> <Type> <Stack implementation> additionally declares the return type; alias <Keys pattern> => <Key aliasName> specifies the registered name; omitting the implementation stack gives a bodyless declaration that only registers the signature.

The first key of a named definition is the function name; an anonymous definition sits on the right side of an assignment and evaluates to an Alias object.

alias add <Number a> and <Number b> -> Number { a + b }
const greet = alias <String name> -> String { 'Hello, ' .. name }

In the first line, the name is the first key add, and and is an auxiliary keyword between formal parameters. In the second line, the right side is an Alias object, and .. is the string concatenation operator.

remove, disable, and enable remove, disable, and enable an alias, respectively. When several patterns match the same code, the longer pattern wins.

2. Function Invocation​

A syntax-style call writes the arguments according to the defined pattern, and is the preferred way to call.

add 1 and 1
greet 'Alice'
const sum = add 1 and 1
print (add 1 and 2)

The right side of the third line is the value of a syntax-style call. In the fourth line, the nested call in argument position is delimited by parentheses.

When the call target is determined only at runtime, or a syntax-style call cannot be written in expression position, use call for dynamic dispatch. call supports three forms: call <Alias>, call <Alias> [<Array>], and call <Alias> *[<Object>].

call add [1, 2]
call add *[a: 1, b: 2]
call greet ['Alice']

Alia has no function-style call syntax; neither add(1, 1) nor comma-separated call arguments are supported.

3. Anonymous Functions​

An anonymous function is defined on the right side of an assignment, and its value is an Alias object.

const add = alias <Number a> and <Number b> -> Number { a + b }
add 1 and 1
call add [1, 2]

The second line is a syntax-style call, and the third passes arguments by position in an array; both use the same argument order. Omitting and means the pattern does not match.

4. Higher-Order Functions​

An <Alias> type parameter accepts an Alias object — an alias literal, a variable holding an Alias, or a named alias identifier. Passing a non-Alias value such as a number or stack literal raises a type mismatch error at check time and a type error at run time.

const applyOperation = alias <Alias op> and <Number a> and <Number b> -> Number { call op [a, b] }
const add = alias <Number x> and <Number y> -> Number { x + y }
call applyOperation [add, 5, 3]

The target of call op [a, b] is the variable op, whose value is determined at runtime. The value of (call applyOperation [add, 5, 3]) is 8.

In a definition whose return value is an Alias, the inner literal is the value at the end of the stack.

const createMultiplier = alias <Number factor> -> Alias {
const inner = alias <Number num> -> Number { num * factor }
inner }
const double = createMultiplier 2
print (double 5)

The value of (double 5) is 10.

5. Function Parameters​

The value of a <Stack> parameter is a stack literal, a variable holding a Stack, or an expression returning a Stack. <Type?> is a nullable type whose value is a value of the target type or Null, and ?? is the null coalescing operator. <Value> is generic; type narrowing before operations is done via typeOf, Ok, and as.

Auxiliary keywords serve as separators in patterns and built-in method calls. and joins parallel parameters, to separates ranges, with separates the target, at separates the position, or provides an optional default, limit provides an optional limit, in and from identify containers and sources, on and as identify join columns and roles, row and column identify row and column positions, and named, keyed, and returning are verb-like labels.

arr.slice 0 to 5
Object.get config keyed 'name' or 'fallback'
str.split ',' limit 2
Math.randomInt from 1 to 10
sheet.lookup 2 returning 'city' in 'id'
sheet.getCell at row 0 column 'name'
each item in arr { item }
str.replace 'old' with 'new'
Math.max 1 and 2 and 3
users.join depts on 'dept_id' and 'dept_id' as 'inner'
new Person named 'Alice' aged 30
'42' as Number
import 'fs' as fs

Basic and optional parameters are defined as follows.

alias calculate <Number a> and <String op> and <Number b> -> Number {
if op == '+' { return a + b } else { return a - b }
}
const greetOptional = alias <String name> and <String? greeting> -> String { (greeting ?? 'Hello') .. ', ' .. name }
calculate 10 and '+' and 5
greetOptional 'Bob' and 'Hi'
call greetOptional ['Alice', null]

The value of (calculate 10 and '+' and 5) is 15. The value of (greetOptional 'Bob' and 'Hi') is 'Hi, Bob'. The value of call greetOptional ['Alice', null] is 'Hello, Alice'.

6. Recursive Functions​

A recursive function calls itself by name inside its definition; parentheses delimit the order of operations of nested calls.

alias factorial <Number n> -> Number {
if n <= 1 { return 1 } else { return n * (factorial n - 1) }
}
print (factorial 5)

The value of (factorial 5) is 120. return <Value> indicates an early return; a parameterless return corresponds to returning 0; the value at the end of a stack is the implicit return, and a trailing return is equivalent to the falling value.

7. Example Code​

alias square <Number num> -> Number { num * num }
alias sumArray <Array arr> -> Number {
let sum = 0
let i = 0
while i < arr.length {
sum = sum + (arr[i] as Number)
i ++
}
sum
}
const isEven = alias <Number num> -> Boolean { num % 2 == 0 }
alias filterArray <Array arr> and <Alias predicate> -> Array {
let result: [Value, ...] = []
for { let i = 0 } i < arr.length { i ++ } { if call predicate [arr[i]] { result.push arr[i] } }
result
}
const numbers = [1, 2, 3, 4, 5]
print ('Square of 5: ' .. (square 5))
print (sumArray numbers)
print (call filterArray [numbers, isEven])
for { let k = 0 } k < 3 { k ++ } { print k }

The sum and result at the ends of the stacks are implicit returns. The value of (square 5) is 25. The value of (sumArray numbers) is 15.

Next Steps​