Functions

Functions are the unit of code in Refingere: every statement lives in one, and a program starts by calling one. This page covers defining and calling them, lambdas, using functions as values, methods, and generators. The Function Definition reference has the exact grammar.

Defining a function

function, a name, parameters in parentheses, and the return type after a :. Every parameter says its type. A function with no return type returns Undefined, and can end without a return. A function that can fail says so in its return type, like Error ! U8 - see Errors.

A name in backticks can hold spaces and symbols. It's mostly used for tests, where the name is the description, and it's called by the same name: `area of a unit square`().

Parameters and arguments

A parameter can have a default after :=, which can use the parameters before it. Arguments are matched by position, or by name with name := value, and once one is named the rest can come in any order. Naming arguments is the usual way to skip a default or make a call with several values of the same type readable. See Call Arguments.

A parameter can take its value apart instead of naming it whole. {x, y} takes fields from a struct, (a, b) takes elements from a tuple, and [first, ...rest] takes elements from a list. All three take one ...rest anywhere, collecting whatever the other elements don't cover, and x as y on any element - it renames a struct field, and gives a tuple or list element a second name. The same patterns work in let and in lambdas. See Argument Destructuring and Pattern Matching.

Generic functions

Type parameters go in angle brackets after the name. A constraint like T: Comparable says what a type argument has to implement, and so what the body can do with its values. The type arguments are usually inferred from the arguments, and can be written out when they can't be. See Types and Interfaces.

Lambdas

A lambda is a function without a name, written where it's used: λ (or lambda), optional parameters, and a body. The body is one expression, whose value is the result, or a block with return. The parameter types can usually be left out, since they come from where the lambda is passed. Lambdas capture the names around them, so λ(n) n > limit can use a local limit. See Lambda.

!! can't be used inside a lambda, since it would return from the lambda and not from the function around it. A lambda that can fail returns the error as its result, and the caller handles it.

Functions as values

Functions, lambdas and operators are all values. Lambda<S64, S64> is the type of one taking an S64 and returning one, and Lambda<(S64, S64), Boolean> takes two. An operator in parentheses, like (+), is the function behind it.

Three operators connect functions. x |> f is f(x), which lets a chain of steps read left to right. xs ||> f applies f to each element, and f >> g makes a function that runs f and then g. See Operators.

Methods and type functions

A function can belong to a type. method name for Type takes the value as its first parameter, self, and is called on a value: body.fahrenheit(). function name for Type has no self and is called on the type, which is how constructors other than Type{...} are written. There's no implicit receiver, so a field is always self.field.

Either can be written in any file, not only next to the type. Methods that fulfil an interface go in an implement block instead. See Interfaces and Method Definition.

Definitions inside functions

Functions, structs, enums and the other definitions can be written inside a block, where they're visible only to the rest of that block. It keeps a helper next to its one use without adding a name to the whole folder. See Inline Definition.

Generators

A function whose body contains yield is a generator. Calling it doesn't run the body: it returns an Iterator, and each value taken from the iterator runs the body up to the next yield. The sequence ends when the body does. Nothing runs until a value is asked for, so a generator can describe a sequence without end and let the caller take as much as it needs. A defer marked @onYield runs at every pause. See Yield.

FizzBuzz as a generator: an endless sequence of answers, of which the test takes the first fifteen. #builtin.forever() counts up without end, and the loop's skipping modifier leaves out zero - see Loop modifiers.

Attributes can say more about a function: @pure promises its result depends only on its arguments, @memoize caches its results, and @main and @test make it an entry point or a test, and @async lets it pause at await while other work runs - see Async. See Attributes.