5. Functions and actions
Both pure functions and actions use the keyword def; the ! suffix on the name decides which it is. These are the first two of Hanki's three layers (§1, The three layers). The third layer, actors (§15), is built from actions.
A pure function has no side effects and may not call any action.
def sum(x: i32, y: i32) -> i32
x + y
end
An action may have side effects. Action names always end in !. The function type takes an optional effect annotation […] after the return type.
use io
def print_hello!(name: string) -> () [io]
io.print!("Hello, #{name}")
end
Rules:
- A
defwhose name does not end in!is a pure function. No effect annotation allowed. - A
defwhose name ends in!is an action. The effect annotation[…]is optional. An action that writes none still has the implicit[local](§6), which makes it non-substitutable and bars a puredeffrom calling it. - A pure
defcannot call any action. The!suffix at the call site is a visible reminder. - An action can call pure functions and other actions whose effects are a subset of its own.
- Types are mandatory on all parameters and the return type.
- A parameter may declare a default after its type, and a defaulted parameter follows every required parameter.
selftakes no default; a parameter without a default after a defaulted one, and a default onself, areH0103. - A call passes between the required count and the full count, and each omitted parameter takes its default. A count outside that range is
H0533. A default evaluates at each call that omits the argument, in the caller, after the explicit arguments, and it sees every earlier parameter's value; it resolves names in the declaring module's scope,_private names included, and a call-site binding cannot shadow them. A binding inside a default is local to it. - A call resolved to a declaration may name its arguments,
connect!(host, timeout_ms=100). Positional arguments come first and fill parameters in order, then keyword arguments follow in any order; a positional argument after a keyword argument isH0581. A keyword names a parameter of the callee (H0579), no parameter is supplied twice (H0580), and every required parameter is supplied (H0533, naming it). An omitted defaulted parameter takes its default wherever it sits. Arguments evaluate in the order written and pass in parameter order. The receiver fillsself, which no keyword names, and a trait method takes the trait's parameter names whichever impl the call resolves to. A trailing-block closure (§13) is the one argument that may follow a keyword argument. The parameter names of an exported callable are part of its interface, and renaming one is breaking (§23). - A
prop, a test or property-test parameter,on actor_died,on signal,migrate, an effect orprovideop, an@intrinsicdeclaration and an impl method of a trait take no default (H0578). - A function value of a callable with defaults presents the arity the expected function type selects, from the required count to the full count; with no expected function type it presents the required count, and an expected count outside that range is
H0533. A value of reduced arity behaves as a closure written at the reference, and each call through it evaluates the omitted defaults. - A call's argument list and a list literal take no trailing comma.
f(a, b,),[1, 2,]and a struct or constructor call ending in a comma areH0103.
def connect(host: string, port: int = 443) -> Conn
# ...
end
connect("example.org") # port is 443
connect("example.org", 8443) # port is 8443