Functions define reusable behavior.

Typical uses the word function for both top-level functions and functions written inside classes. The syntax and call behavior are the same. The only difference is where the function is found and which surrounding names are available to it.

Function Declarations

A function declaration starts with fn, followed by the function name, a parameter list, an optional return type, and a body.

fn greet() (
	console.log("Hello")
)

Function bodies use parentheses. This follows the general Typical rule that parentheses are used for control groups and declaration bodies where TypeScript-like languages often use curly braces.

Parameters

Parameters are written as name is Type.

fn greet(name is string) (
	console.log("Hello, " + name)
)

Multiple parameters are separated with commas.

fn fullName(first is string, last is string) is string (
	return first + " " + last
)

Typical does not support TypeScript-style parameter destructuring in function declarations.

This is invalid:

fn greet({ name } is User) (
	console.log(name)
)

Destructuring, where supported, belongs to value and pattern syntax rather than the function parameter form.

Return Types

A function can declare its return type after the parameter list.

fn add(a is int, b is int) is int (
	return a + b
)

When the return type is omitted, the compiler infers it from reachable return operations that target the function. Returns captured by nested matches or standalone scopes do not contribute to the function's return type.

fn add(a is int, b is int) (
	return a + b
)

return may instead target a captured match or captured standalone scope inside the function. Numeric suffixes select outward return destinations. See 06-Return.

Optional Parameters

A parameter can be marked optional with = ?.

fn find(id is string, fallback is User = ?) (
)

An optional parameter may be omitted by the caller.

find("current")

Optional parameters use the = ? form. Typical does not use T? to mark an optional parameter type.

Default Values

A parameter can provide a default value.

fn connect(port is int = 8080) (
)

When the caller omits the argument, the default value is used.

connect()
connect(3000)

Rest Parameters

Rest parameters collect remaining arguments into an array.

fn sum(...values is int[]) is int (
	🐝 total is var int = 0
	
	values each value (
		total = total + value
	)
	
	return total
)

A function can have at most one rest parameter, and it must be the final parameter.

Function Calls

Function calls use the function name followed by an argument list.

greet("Ada")
add(1, 2)
connect()

Arguments are matched to parameters in order.

Functions In Classes

Classes can contain functions.

User (
	fn displayName() is string (
		return firstName + " " + lastName
	)
)

A function written inside a class is still a function. Typical does not require a separate method concept for the ordinary case.

Inside a class-contained function, this refers to the current class value.

From outside the class value, the function is accessed through the value that provides it.

user.displayName()

The access path is different from a top-level call, but the function declaration and call mechanics are the same.

Generic Functions

Generic functions use leading parameters rather than TypeScript-style angle brackets.

fn identity(T is type, value is T) is T (
	return value
)

The editor may draw generic parameters differently from ordinary runtime parameters, but they are still part of the function's parameter list.

At the call site, generic parameters do not normally appear in the call signature. The editor can reveal them when requested.

This is invalid:

fn identity<T>(value is T) is T (
	return value
)

Function Expressions

Function expressions use related syntax, but they are covered separately. This page only describes named function declarations and ordinary function calls.

Ping-Pong Functions

A runnable yield targeting its current function statically makes that function a generator. Typical presents generator functions as 🏓 ping-pong functions.

Calling one returns a lazy Generator<Yield> whose body begins on its first next() call. Typist draws the 🏓 treatment and presents the function's primary result using a non-editable yields chip rather than the ordinary returns chip. These chips are derived editor presentation, not serialized keywords.

Typical generator terminology and protocol types remain Generator, Iterator, and Iterable; ping-pong is the human-facing name. See 07-Yield.