Overview
You declare functions using the fn
statement. The two forms of function declarations in MUFL are:
<name> = fn (args) [-> return-type] = (expression). // expression form
<name> = fn (args) [-> return-type] { code } // block form
Another convenient form for these declarations is:
fn <name> (args) [-> return-type] = (expression). // expression form
fn <name> (args) [-> return-type] { code } // block form
Valid Locations for Function Declarations
Above, name
refers to a variable that holds the function. The same variable is used to invoke it later.
Grammatically, function declarations can be used in variable declarations as above, as well is in other contexts, for example, when defining a dictionary:
d = ("increment"->fn (x: int) { return x+1. }, "decrement"->fn (x: int) = (x-1)).
Additionally, functions in expression forms can be used in any expression. For example:
_print (fn (x:int) = (x+1) 1). // define a function and immediately invoke it, then print the result
Prints:
2
Returning a Value
If the return type is declared, then the value the function returns must be of a compatible type.
For block-form functions, return
terminates evaluation of a function and returns immediately.
If a function does not have a return
statement in its body, then the value of the last statement executed in the body is the return value.
Expression-form functions always return the result of the expression and infer the return type when omitted from the expression.
Arguments
Function arguments take the form [safe]name[:type]
. For example:
F = fn (x:int) = (x+1).
If the type of an argument is omitted, the argument is treated as having type any
.
Type Destructuring in Arguments
When the argument type is record
or tuple
, function declarations allow pattern destructuring in argument declaration.
fn F (record:($field1->arg1:int, $field2->arg2:str))
{
_assert (record $field1 == arg1 && record $field2 == arg2).
}
A more verbose alternative is:
fn F (record:($field1->int, $field2->str))
{
bind ($field1->arg1, $field2->arg2) to record.
_assert (record $field1 == arg1 && record $field2 == arg2).
}
The safe Attribute
Adding the safe
attribute to an argument of a function instructs the MUFL compiler to generate runtime type verification for the given argument, essentially inserting a safe cast into the generated code.
Invoking Functions
Having assigned a function to a name, you can use this name to invoke the function using Curry notation:
fn increment (x:int) = (x+1).
_assert (increment 5 == 6).
Binding Values
Functions in MUFL act much like lambda values in other functional languages. Local variables are always captured by value. The important exception to general norms of functional programming is that global variables are always captured by reference. Consequently, functions can be used to mutate global variables.
For more information, refer to Using Reference to Mutate Data.