STAGE 1 / Kujo foundations · LESSON 06
Mental model
A function defines an operation and the parameters it accepts. The caller should be able to understand what input is needed and what output means without reading every statement in the body.
Language contract
Use func name(parameters) { ... }. Arguments are positional and arity matters. An explicit return value supplies a result. Reaching the end of a function returns null; the last expression is not an implicit return value. A bare return also yields null.
Functions are values. Anonymous func(...) { ... } expressions can be passed to other functions, enabling callbacks. A closure resolves captured names from its lexical context. Treat capture behavior as Kujo behavior, not as an imported Python or JavaScript equivalence; the next stage examines it more closely.
Read the example
apply knows how to invoke an operation, while the anonymous function defines a calculation. Separating them lets a caller change the policy without rewriting the mechanism. The second function deliberately falls through to expose null.
Professional pattern
Keep domain calculations pure: pass values in, return values out. Put file reads, network requests, and printing at the application's edge. This lets tests call the calculation without granting host authority and makes later model-proposed inputs easier to validate.
Do not make a callback quietly depend on a large set of global variables. Pass context explicitly or capture only the bindings the callback needs, so its inputs remain inspectable. For a tool handler later, this distinction becomes a security concern: a tool description is not permission to access any global resource.
Common mistakes
Printing a value does not return it. A caller receiving null may indicate a missing return, not a broken arithmetic operation. Likewise, supplying extra arguments is not a portable way to attach optional context; use the exact signature or a deliberate dictionary parameter.
Working example
func apply(value, operation) { return operation(value) }
let doubled := apply(6, func(x) { return x * 2 })
func no_result() { let local := 3 }
assert_equal(doubled, 12)
assert_equal(no_result(), null)
print(doubled)
Run it
From the course repository root, use the pinned Kujo 1.3.1 runtime.
kujo check examples/06.kujo
kujo run --untrusted examples/06.kujo
Captured output
12
Break it and diagnose it
The function requires two positional arguments. The failure concerns arity, not the arithmetic inside the function.
func add(a, b) { return a + b }
print(add(1))
kujo run --untrusted examples/06-break.kujo
Exit status: 4. Captured diagnostic:
[KUJOVM001] [vm] Runtime Error: add expects 2 arguments, got 1
--> 0:0
Exercise
Write a summarize(values, predicate) function. Pass two predicates that accept different values. Keep printing outside the function and test the returned summary directly.
Checkpoint
- I use explicit return when a result is required.
- I can pass a function as a value.
- I can explain a callback’s inputs and captured context.
Verified 2026-09-06 · Official Kujo 1.3.1 release · Source contract · Download example