Early preview: syntax, standard library APIs, and tooling may change.

FoundationsFunctions

Functions

Functions make data flow explicit with parameter and return types. Purity is optional: func declares an impure function; pure func adds a compiler-enforced purity contract.

Parameters

Parameters are written as name: Type inside parentheses. In add(a: Int, b: Int), the two arguments are bound to a and b inside the function.

func add(a: Int, b: Int) -> Int:
	a + b


func main(args: List[String]) -> Void:
	print(add(2, 3).to_string()) -- prints: 5

Return Types

The return type follows ->. The last expression supplies the returned value. For named functions, omitting -> means Void, not an inferred result type; Void means no returned value.

pure func label(score: Int) -> String:
	"score: " + score.to_string()

Pure and Impure Functions

Impure functions may perform I/O and call either pure or impure functions. Pure functions cannot call impure functions. The Pure Functions page explains the restricted exception for debug diagnostics.

pure func square(value: Int) -> Int:
	value * value


func main(args: List[String]) -> Void:
	print(square(4).to_string()) -- prints: 16

Functions as Values

Functions are values: bind a named function or pass it as an argument. A callback type such as (Int) -> Int specifies its input and output; pure (Int) -> Int also requires purity.

func increment(value: Int) -> Int:
	value + 1


func apply(value: Int, transform: (Int) -> Int) -> Int:
	transform(value)


func main(args: List[String]) -> Void:
	next: (Int) -> Int = increment
	print(next(10).to_string()) -- prints: 11
	print(apply(10, next).to_string()) -- prints: 11

Anonymous Functions

func(value): expression defines a callback inline. Its parameter types can come from the expected function type. pure func(...) explicitly declares a pure callback; an expected pure type can also infer purity when the body has no impure operations.

func main(args: List[String]) -> Void:
	increment = func(value: Int) -> Int: value + 1
	print(increment(4).to_string()) -- prints: 5
	add: (Int, Int) -> Int = func(left, right): left + right
	print(add(2, 4).to_string()) -- prints: 6
	double: pure (Int) -> Int = pure func(value: Int): value * 2
	print(double(4).to_string()) -- prints: 8
	-- The expected pure function type also permits purity inference.
	triple: pure (Int) -> Int = func(value): value * 3
	print(triple(3).to_string()) -- prints: 9

Local Helpers

Use a small helper when the public function wants a simpler interface than the implementation.

pure func outer(n: Int) -> Int:
	pure func inner(x: Int) -> Int:
		x + 1
	inner(n)

Recursion

Recursive functions call themselves directly.

pure func count_down(n: Int) -> Int:
	if n == 0:
		0
	else:
		count_down(n - 1)

@tail_recursive

@tail_recursive asks the compiler to verify the recursive call is in tail position.

@tail_recursive
pure func loop(n: Int, acc: Int) -> Int:
	if n <= 0:
		acc
	else:
		loop(n - 1, acc + n)

Example

factorial.brp
func factorial(n: Int) -> Int:
	if n <= 1:
		1
	else:
		n * factorial(n - 1)


@tail_recursive
func factorial_loop(n: Int, acc: Int) -> Int:
	if n <= 1:
		acc
	else:
		factorial_loop(n - 1, n * acc)


func main(args: List[String]) -> Void:
	print(factorial(5)) -- prints: 120
	print(factorial_loop(5, 1)) -- prints: 120

Functions and Callbacks

function-values.brp
func add(left: Int, right: Int) -> Int:
	left + right


pure func square(value: Int) -> Int:
	value * value


func increment(value: Int) -> Int:
	value + 1


func apply(value: Int, transform: (Int) -> Int) -> Int:
	transform(value)


func main(args: List[String]) -> Void:
	print(add(2, 3).to_string()) -- prints: 5
	print(square(4).to_string()) -- prints: 16
	next: (Int) -> Int = increment
	print(apply(10, next).to_string()) -- prints: 11
	print(apply(10, func(value): value + 2).to_string()) -- prints: 12

Try it

terminal
blorp run factorial.brp