Functions
Functions in Do are first-class values. They close over their lexical scope and can be stored in variables, passed as arguments, and returned from other functions.
def
Define named functions with def:
Decorators
Definitions may be preceded by one or more decorators using #[expr] syntax:
Each decorator expression is evaluated in the surrounding scope. After the function value is created, decorators are applied from bottom to top, with each decorator receiving the current value and returning a replacement.
The same syntax also applies to class definitions. See
Classes for the common
#[field.setter] property pattern.
Implicit Return
A function returns the result of its statement or block implicitly. Every statement produces a result:
| Statement | Result |
|---|---|
| command | function return value |
let |
value of right-hand side |
bind |
value of scrutinee |
if (with final else) |
result of branch |
if (without final else) |
nil |
try/catch |
result of try (no error) or invoked catch |
All other statements have a nil result. The result of the last statement in a
block becomes the block's result, and thus that of the function call.
Use return for early exit:
Parameters
Positional Parameters
Keyword Parameters
Keyword parameters use key: param syntax in the definition:
Ditto Key Shorthand
The :key shorthand declares a key parameter bound to a variable of the
same name:
The shorthand also works at call sites to pass a variable as a key argument:
let name = Alice
let age = 30
create_user :name :age
# equivalent to: create_user name: $name age: $age
Default Values
Both positional and key parameters support defaults:
def greet name = "World"
echo "Hello, $name!"
greet() # Hello, World!
greet Alice # Hello, Alice!
def connect :host = localhost :port = 8080
echo "Connecting to $host:$port"
connect() # localhost:8080
connect port: 3000 # localhost:3000
connect host: example.com # example.com:8080
Defaults are instantiated on every invocation of the function, so the following
function always returns a fresh empty array if called with no arguments:
Variadic Parameters
Use ... to accept extra arguments:
The rest parameter receives an argument iterator that yields positional and
key arguments in invocation order. When iterating arguments manually, each item
is a [key, value] pair where the key is the symbol for arguments and the
positional argument index (0-origin) for positional arguments.
def echo_all ...args
for k v = args
echo "$k: $v"
echo_all foo bar: 1 baz
# prints:
# 0: foo
# bar: 1
# 1: baz
Argument Spreading
Spread an iterable into a call:
let args = [1, 2, 3]
func ...args
# equivalent to: func 1 2 3
let kwargs = {name: "Alice", age: 30}
func ...kwargs
# equivalent to:
# func name: "Alice" age: 30
Vertical Parameter Layout
Parameters in def can use vertical layout:
do Blocks
Anonymous functions (blocks and lambdas) are created with do:
Statement Context
In statement context, do without a following newline creates a one-statement
block:
With parameters:
If an immediate newline and indented block follows, it creates a multi-statement block:
Expression Context
In expression context, do creates a lambda where the body is an expression:
Non-Local Flow Control
break, continue, and return work through do blocks:
breakexits the innermost enclosing loopcontinueskips to the next iteration of the innermost enclosing loopreturnexits the innermost enclosingdef
This allows natural flow control in callbacks and higher-order functions:
def validate_record record
for field = ["id", "name", "email"]
record.get field else: do
return {valid: false, missing: field}
{valid: true}
A non-local branch will only be effective for the duration of the statement that introduces its containing closure; after this it will propagate a runtime error. The compiler requires that a closure containing non-local branches be in argument position to reduce the likelihood of this sort of error:
# Valid: closure is passed as an argument
def validate record
record.get "name" else: do
return false
true
# Invalid: closure is bound to a variable first
def bad_example
# Compiler will reject this line
let closure = do return false
record.get "name" else: $closure
Public Functions
Use pub def to export a function from a module:
See Modules for details on the module system.