Functions
This page covers function definitions, calling conventions, signatures, and advanced patterns in TypR.
Defining typed functions
The fn keyword defines a typed function. Parameter types use : type, and the return type appears after the closing parenthesis:
let add <- fn(a: int, b: int): int {
a + b
};
print(add(5, 3));
fn(...) always requires a return type — omitting it triggers an explicit panic ("You forgot to specify the function return type").
Default parameters
Default values are supported for the final parameter(s):
let greet <- fn(name: char, greeting: char = "Hello"): char {
greeting
};
greet("World"); # "Hello"
greet("World", "Hi"); # "Hi"
Variadic functions
let sum_all <- fn(...xs: int): int {
/* ... */
};
The ... prefix makes a parameter accept any number of arguments.
Calling conventions
TypR supports three equivalent ways to call a function, thanks to the uniform function call syntax (UFCS):
# Classic call
print(add(5, 3));
# Pipe syntax (|>)
(5) |> add(3)
|> print();
# Method call syntax (.)
(5).add(3)
.print();
All three styles produce the same result. The first argument can be "pulled out" as the receiver in pipe or method-call notation — making operation chaining readable and natural.
Lambdas
Lambdas use \ without type annotations:
let sq <- \(x) x * x;
\(...) (lambda) has no declared parameter types or return type. Use fn(...) when types are needed.
Partial application
The \ symbol also supports partial application of functions:
let add5 <- \add(a = 5); # partial application of a function
let origin <- \Point:{ x = 0, y = 0 }; # partial application of a record constructor
Lambda vs partial application: both use \, disambiguating by what follows — \( → lambda, \identifier( → partial application. PartialApp is desugared into Lang::Function during type-checking and never reaches transpilation.
Higher-order functions
Functions are first-class values in TypR. They can be passed as arguments and returned as values:
type Function <- (int) -> bool;
let function0 <- fn(a: int): bool {
true
};
The type system tracks function types using (T1, T2) -> T3 syntax, ensuring composition and callbacks are type-safe.
Closures
Functions can capture variables from their surrounding environment. The type system ensures captured variables and returned functions remain type-safe:
let make_adder <- fn(n: int): (int) -> int {
fn(x: int): int { x + n }
};
let add5 <- make_adder(5);
add5(3); # 8
Signatures: typing existing R functions
By default, most base R functions accept Any and return Empty. The @ annotation declares the type of an existing R function without modifying it:
@toupper: (char) -> char;
toupper("Hi"); # now takes char, returns char
# toupper(7); # would now fail at compile time
See Signatures, @extern & Foreign for overloading, @extern, and @importFrom.
Interfaces: polymorphic functions
Interfaces enable ad-hoc polymorphism — write functions that work across multiple types:
@paste: (Any, Any) -> char;
type Viewable <- interface {
view: (Self) -> char
};
let double <- fn(a: Viewable): char {
paste(view(a), view(a))
};
let view <- fn(a: bool): char { "bool" };
true.double() # works because bool implements Viewable
See Interfaces & Structural Validation for details.