Function Declarations
Functions are the primary building blocks for organizing code in Sector9. This page covers the basic func syntax, parameters, return types, body forms, and where verification contracts attach.
Basic Syntax
A function declaration uses the func keyword followed by a name, parameters, return type, and body:
func name(param1 : Type1, param2 : Type2) : ReturnType {
// body
}
The simplest function takes parameters and returns a value:
func add(x : Nat, y : Nat) : Nat {
x + y
}
Functions with no parameters use empty parentheses:
func zero() : Nat {
0
}
Parameters
Typed Parameters
Every parameter requires a type annotation. Multiple parameters are separated by commas:
func greet(name : Text, age : Nat) : Text {
// use name and age
}
Single Parameter
A function with one parameter:
func double(n : Int) : Int {
n * 2
}
No Parameters
Functions with no parameters still require parentheses:
func getTimestamp() : Nat {
0 // placeholder
}
Return Types
Explicit Return Types
The return type follows the parameter list after a colon:
func isEven(n : Nat) : Bool {
n % 2 == 0
}
Unit Return Type
Functions that return no meaningful value use () (unit):
func logMessage(msg : Text) : () {
// side effect only
}
Async Return Types
Public actor methods return async types to support message passing:
public func getData() : async Nat {
42
}
See async functions and await for actor-message control flow.
Body Forms
Sector9 supports two body forms: block bodies and expression bodies.
Block Body
The block body uses curly braces and can contain multiple statements. The last expression is the return value:
func compute(x : Nat, y : Nat) : Nat {
let sum = x + y;
let doubled = sum * 2;
doubled // returned
}
Expression Body
For simple functions, use = followed by a single expression:
func square(n : Nat) : Nat = n * n;
This is equivalent to:
func square(n : Nat) : Nat {
n * n
}
Expression bodies work well for short computations:
func isPositive(x : Int) : Bool = x > 0;
func triple(n : Int) : Int = n * 3;
func maxOf(a : Int, b : Int) : Int = if (a >= b) { a } else { b };
Visibility Modifiers
Public Functions
public makes a function accessible from outside the module or actor:
module {
public func exported() : Nat { 42 }
}
In actors, public functions become callable methods:
persistent actor {
public func greet() : async Text {
"Hello"
}
}
Private Functions
Functions without public are private by default:
persistent actor {
// Private - only callable within this actor
func helper(x : Nat) : Nat {
x + 1
}
public func useHelper(n : Nat) : async Nat {
helper(n)
}
}
Use private explicitly for clarity:
private func internal(x : Nat) : Nat {
x * 2
}
Functions in Different Contexts
Module Functions
Functions in modules are local by default:
module Math {
public func add(a : Nat, b : Nat) : Nat {
a + b
}
// Private helper
func clamp(x : Nat, max : Nat) : Nat {
if (x > max) { max } else { x }
}
}
Actor Methods
Public functions in actors become shared methods that return async types:
persistent actor Counter {
var count : Nat = 0;
public func increment() : async Nat
modifies count
ensures result == count;
{
count += 1;
count
}
}
Actor methods exposed as calls return async types. State-changing public methods also need accurate modifies clauses, and callers that depend on state preconditions should get entry_requires as well as internal requires.
Query Functions
Use query for fast actor methods that are intended to be read APIs:
persistent actor {
var balance : Nat = 0;
public query func getBalance() : async Nat
reads balance
ensures result == balance;
{
balance
}
}
Prefer keeping query functions read-only. If a query body writes actor fields, current verification requires an explicit modifies clause; do not rely on query execution for durable consensus state changes.
Verification Contracts
Functions can include verification contracts in the signature. See Contracts for full details.
Preconditions
requires specifies what must be true when the function is called:
func divide(a : Int, b : Int) : Int
requires b != 0;
{
a / b
}
Postconditions
ensures specifies what must be true when the function returns:
func abs(x : Int) : Int
ensures result >= 0;
{
if (x >= 0) { x } else { -x }
}
Effect Annotations
reads and modifies declare which actor fields the function accesses:
persistent actor {
var total : Nat = 0;
public func add(amount : Nat) : async Nat
modifies total
ensures total == old(total) + amount;
{
total += amount;
total
}
}
Contract Placement
Contracts appear after the return type annotation but before the function body:
func name(params) : ReturnType
requires precondition1;
requires precondition2;
ensures postcondition1;
ensures postcondition2;
{
// body
}
For actor methods with effects:
public func update(x : Nat) : async ()
reads field1
modifies field2
entry_requires x > 0;
requires x > 0;
ensures field2 == old(field2) + x;
{
// body
}
Common Patterns
Returning Early with Conditionals
The last expression is the return value:
func classify(n : Int) : Text {
if (n < 0) {
"negative"
} else if (n == 0) {
"zero"
} else {
"positive"
}
}
Helper Functions
Use private functions to break up complex logic:
persistent actor {
var balance : Nat = 0;
private func validate(amount : Nat) : Bool {
amount > 0 and amount <= 10000
}
public func deposit(amount : Nat) : async Bool
modifies balance
entry_requires amount <= 10000;
requires amount <= 10000;
{
if (validate(amount)) {
balance += amount;
true
} else {
false
}
}
}
Chaining Function Calls
module {
public func step1(x : Nat) : Nat { x + 1 }
public func step2(x : Nat) : Nat { x * 2 }
public func step3(x : Nat) : Nat { x - 1 }
public func pipeline(x : Nat) : Nat {
step3(step2(step1(x)))
}
}
Common Mistakes
Missing Async for Actor Methods
persistent actor {
// WRONG - public actor methods need async
public func bad() : Nat {
42
}
// CORRECT
public func good() : async Nat {
42
}
}
Shared Functions Outside Actors
// WRONG - shared functions must be in actors
shared func badShared() : async () {
// rejected
}
// CORRECT - inside an actor
persistent actor {
public func goodShared() : async () {
// OK
}
}
Contracts Inside Body
// WRONG - contracts go in the signature
func bad(x : Nat) : Nat {
requires x > 0; // syntax error
x
}
// CORRECT
func good(x : Nat) : Nat
requires x > 0
{
x
}
Missing Modifies Clause
persistent actor {
var x : Nat = 0;
// WRONG - modifying x without declaring it
public func bad() : async () {
x := 1; // verification error
}
// CORRECT
public func good() : async ()
modifies x
{
x := 1;
}
}
Related Topics
- Anonymous Functions - Lambda expressions
- Higher-Order Functions - Functions as values
- Pure Functions - Side-effect free functions
- Contracts - Verification annotations
Summary
- Functions use
funckeyword with name, parameters, return type, and body - Block bodies use
{ }and return the last expression - Expression bodies use
= expressionfor simple functions publicmakes functions accessible outside the module or actor- Actor methods return
asynctypes and can includemodifies/readsclauses - Contracts (
requires,ensures) appear between the signature and body - Private helper functions organize complex logic