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:
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.