Functions
Learning Echo from scratch?
Start with Lesson 5 — Functions. This page covers the full function surface (use mut, lambdas, variadics, …).
Named fns with typed parameters. Outer variables can be read lexically. Reassignment of an outer variable needs use mut. A return requires a return type on the function.
Basic Example
fn greet(name: str) {
say("Hello, ${name}!");
}
fn add(a: int, b: int) -> int {
return a + b;
}
greet("Echo");
say(add(2, 3));Output
Hello, Echo!
5How It Works
- Every parameter needs a type annotation.
- Return type annotations are optional only when the function has no
returnstatements. - If a function contains
return(including barereturn;), a return type annotation is required — use-> voidwhen there is no value. - Keyword arguments work on user-defined functions. Most builtins accept them too, except variadic ones (
say,format, …). - Functions can read outer variables without
use. Onlyuse mut name;permits assignment to an outer name.
Keyword arguments
fn describe(name: str, age: int) {
say(name, "is", age);
}
describe(age: 21, name: "Alice");Output
Alice is 21Other shapes
fn addPair([a: int, b: int]) -> int {
return a + b;
}
fn greetUser({ name: str }) -> str {
return name;
}
fn square(x: int) -> int => x * x;
say(addPair([2, 3]));
say(greetUser({ name: "Ada" }));
say(square(4));Output
5
Ada
16Lambdas and collection helpers
double: fn(int) -> int = fn(x: int) -> int { return x * 2; };
say(double(21));
say(map([1, 2, 3], double));
say(reduce([1, 2, 3], 0, fn(acc: int, x: int) -> int { return acc + x; }));Output
42
[2, 4, 6]
6Defaults and trailing variadic
fn join(punct: str = ",", parts: str...) {
say(parts.join(punct));
}
join(parts: ["a", "b"]);
join(" | ", "a", "b", "c");Output
a,b
a | b | cA function with trailing defaults is assignable to the full-arity type and to a narrower type that omits those defaulted parameters:
fn add(x: int, y: int = 0) -> int {
return x + y;
}
full: fn(int, int) -> int = add;
narrow: fn(int) -> int = add;
say(full(2, 3));
say(narrow(2));Output
5
2Builtins as values
print: fn(str) -> dynamic = say;
print("hi");
say(type(say));Output
hi
fnVariadic builtins (say, eprint, format, pathJoin) have type fn(dynamic...) -> void or fn(dynamic...) -> str. Bound methods such as xs.map are values; xs.unknown is still an error.
Common Mistakes
return without a return type — E1009
fn f() { return; }Fails with E1009. Use an explicit return type:
fn f() -> void { return; }Assigning an outer variable without use mut — E2003
count: int = 0;
fn bump() {
count = count + 1;
}
bump();Reading count would work; assigning it needs use mut count; inside the function.
use alone does not allow assignment
count: int = 0;
fn bump() {
use count;
count = count + 1;
}Still E2003. Only use mut count; permits the assignment.
Keyword args on variadic builtins — E2601
say(msg: "hi");Fails with E2601 (say is variadic and does not take keyword arguments).
Current Limitation
- No overloads
