Sentinel
Language: Functions
Functions allow you to create reusable code to perform computations.
Below is a trivial example function that doubles a number and shows the main rule using the function:
Sentinel Playground
Loading the playground...
Press "Run" to get policy output
Functions may contain any expressions, conditionals, and loops. They must return a value at the end of their execution. If no reasonable return value exists, the function should return undefined.
Function Types
Named Functions
NOTE: Named functions must be created within the package scope.
Named functions are declared using the func
keyword as its own statement.
They provide a safe method of creating functions and have additional
restrictions that do not apply to anonymous functions.
Firstly, named functions cannot be re-assigned, and also cannot use a name that is already used elsewhere. For instance, the below example will error due to the attempt to reassign the named function identifier to a new value:
func sum(a, b) {
return a + b
}
sum = 4
Additionally, the following will error due to the named function attempting to make use of an already assigned identifier:
sum = 4
func sum(a, b) {
return a + b
}
Named functions are helpful for policy authors to declare critical functions whose value or implementation should not be changed.
Anonymous Functions
An anonymous function is created by assigning a variable to a func
. The
variable can be reassigned at any time including to different value types.
Anonymous functions are helpful for use cases like closures, where a function
can return another function.
func makeAdder(a) {
return func(b) {
return a + b
}
}
Creating a Function
A function can have zero or more parameters. These parameters are specified
within parentheses ()
separated by commas. If a function has no parameters,
the parentheses must still be present.
A function must terminate with a return
statement to return a value back to
the caller. Only a single value can be returned. The type of a return can
vary between return statements.
Anonymous function example:
add1 = func(x) { return x + 1 }
Named function example:
func add1(x) {
return x + 1
}
Both examples create a function that adds 1 to the parameter x
.
Calling a Function
Functions are called by accessing their name followed by parentheses with a list of comma-separated values for the parameters. If the function takes no parameters, an empty set of parentheses must still be used to denote a function call.
To call the function in the example above:
x = 1
y = add1(x)
Scoping
The body of a func
creates a new scope.
The parent scope is the scope in which the function is created. This allows for the creation of functions within functions that can access their outer scopes.
Example:
f = func() {
a = 42
print(a) // 42
return undefined
}
print(a) // undefined
a = 18
f = func() {
a = 42
return undefined
}
print(a) // 18
And below is an example of creating a function in a function which uses outer values:
f = func() {
a = 42
double = func() { return a * 2 }
return double()
}
print(f()) // 84
A more complex example below shows how scoping works when passing functions around as arguments or results:
f = func() {
a = 42
double = func() { return a * 2 }
return double
}
double = f()
print(double()) // 84
Pass By Value
The parameters to a function are passed by value, but not deep copied. This means that elements of collections can still be modified but the root value cannot be changed.
Example:
f = func(x) {
x = "value"
return x
}
x = "outside"
f(x)
print(x) // "outside"
f = func(x) {
append(x, "value")
return x
}
x = []
f(x)
print(x) // ["value"]
Recursion
A function is allowed to call other functions, including itself. This can be used to implement recursion. For example, the fibonacci sequence is implemented below:
fib = func(x) {
if x <= 0 {
return undefined
}
if x == 1 {
return 1
} else {
return x + fib(x - 1)
}
}
Note that this example also shows using undefined as a return value in cases where a function has undefined behavior.