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

FoundationsPure Functions

Pure Functions

pure func marks deterministic code and makes programs easy to reason about.

`pure func`

A pure func always returns the same value for the same input. It cannot perform I/O, call impure functions, mutate global state, or accept impure callbacks.

pure func add_one(n: Int) -> Int:
	n + 1

Pragmatic Local Mutation

Local var bindings are allowed when the mutation cannot be observed outside the function.

pure func total(xs: List[Int]) -> Int:
	var sum: Int = 0
	for x in xs:
		sum += x
	sum

Keeping I/O at Program Boundaries

It's often useful to keep impure code limited to the "shell" of a program -- things like reading files, network calls, and printing. The core of your program can focus on pure data processing.

import:
	system: read_file


pure func word_count(text: String) -> Int:
	text
		.words()
		.length()


func main(args: List[String]) -> Int:
	match read_file("notes.txt"):
		Ok(text):
			print(word_count(text))
			0
		Err(msg):
			print_error(msg)
			1

Larger Boundary Example

Rejected Effects

The same file read is rejected inside pure func; the boundary has to stay visible in the caller.

import:
	system: read_file


pure func load_notes() -> Result[String, String]:
	read_file("notes.txt") -- rejected

Pure Callbacks

Collection and parallel APIs can accept pure callbacks when they need deterministic work.

items.parallel(
    -- `pure` can be omitted in closures where pure functions are required
    func(chunk): chunk.map(func(x): x * 2)
)

Example

analyze-notes.brp
import:
	system: read_file


record TextStats {lines: Int, words: Int, todos: Int}


pure func count_note_lines(text: String) -> Int:
	var total: Int = 0
	for line in text.lines():
		if line.trim() != "":
			total += 1
	total


pure func count_todos(text: String) -> Int:
	var total: Int = 0
	for line in text.lines():
		if line.contains("TODO"):
			total += 1
	total


pure func analyze(text: String) -> TextStats:
	{
		lines = count_note_lines(text),
		words = text
			.words()
			.length(),
		todos = count_todos(text)
	}


pure func format_stats(stats: TextStats) -> String:
	"lines: " + stats.lines.to_string() + ", words: " + stats.words.to_string() + ", todos: " + stats.todos.to_string()


func main(args: List[String]) -> Int:
	path: String = "notes.txt"
	match read_file(path):
		Ok(text):
			print(format_stats(analyze(text))) -- prints: lines: 3, words: 11, todos: 2
			0
		Err(msg):
			print_error(msg)
			1

Try it

terminal
blorp run analyze-notes.brp