Cadence Mastery: Functions & Scope

1. Functions

Functions are sequences of statements that perform a specific task. Functions have parameters (inputs) and an optional return value (output). Functions are typed: the function type consists of the parameter types and the return type.

Functions are values, i.e., they can be assigned to constants and variables, and can be passed as arguments to other functions. This behavior is often called first-class functions.

1.1 Declarations

Functions can be declared by using the fun keyword, followed by the name of the declaration, the parameters, the optional return type, and the code that should be executed when the function is called.

The parameters need to be enclosed in parentheses. The return type, if any, is separated from the parameters by a colon (:). The function code needs to be enclosed in opening and closing braces.

Argument labels precede the parameter name. The special argument label _ indicates that a function call can omit the argument label. If no argument label is declared in the function declaration, the parameter name is the argument label of the function declaration, and function calls must use the parameter name as the argument label.

There is no support for optional parameters, i.e. default values for parameters, and variadic functions, i.e. functions that take an arbitrary amount of arguments.

fun double(_ x: Int): Int {

return x * 2

}

double(2) // is 4

The order of the arguments in a function call must match the order of the parameters in the function declaration.

Functions do not support overloading.

fun test(first: Int, second: Int) {

// ...

}

// Invalid: the arguments are provided in the wrong order

test(second: 1, first: 2)

1.2 Expressions

Functions can be also used as expressions. The syntax is the same as for function declarations, except that function expressions have no name, i.e., they are anonymous.

let double =

fun (_ x: Int): Int {

return x * 2

}

1.3 Calls

Functions can be called (invoked). Function calls need to provide exactly as many argument values as the function has parameters.

fun double(_ x: Int): Int {

return x * 2

}

double(2) // is 4

2. Closures

A function may refer to variables and constants of its outer scopes in which it is defined. It is called a closure, because it is closing over those variables and constants. A closure can read from the variables and constants and assign to the variables it refers to.

// Declare a function named makeCounter which returns a function that

// each time when called, returns the next integer, starting at 1.

fun makeCounter(): ((): Int) {

var count = 0

return fun (): Int {

count = count + 1

return count

}

}

let test = makeCounter()

test() // is 1

test() // is 2

3. Function Preconditions and Postconditions

Preconditions must be true right before the execution of the function. Preconditions are part of the function and introduced by the pre keyword, followed by the condition block.

Postconditions must be true right after the execution of the function. Postconditions are part of the function and introduced by the post keyword, followed by the condition block. Postconditions may only occur after preconditions, if any.

4. Scope

Every function and block ({ ... }) introduces a new scope for declarations. Each function and block can refer to declarations in its scope or any of the outer scopes.

let x = 10

fun f(): Int {

let y = 10

return x + y

}

f() // is 20

// Invalid: the identifier y is not in scope.

y

Each scope can introduce new declarations, i.e., the outer declaration is shadowed.

Scope is lexical, not dynamic.