Skip to main content

Functions

Functions are defined with fn:

fn add(a, b) {
return a + b
}

returns can be implicit; a function returns whatever its body evaluated to. Short functions are commonly written on one line:

fn add(a, b) { a + b }

A bare return, or an empty body, returns nil.

Type annotations

x: Foo is a type annotation, which is always optional. Plain x in a signature is the same as x: Any.

fn pow(x, n: Int) {
r = one(x)
while n > 0 {
n = n - one(n)
r = r * x
}
return r
}

Annotations aren't needed for performance – unannotated code is fully type-inferred. Their main job is dispatch: a function can have many definitions, and annotations (and richer patterns) decide which one handles a given call. That's a big enough topic to get its own chapter.

Splats

The ellipsis ... collects or spreads multiple arguments, known as splatting. In a signature it collects trailing arguments into the function; in a call it spreads a collection out into arguments.

fn seq(x, xs...) { prepend(seq(xs...), x) }

The splat need not be the only argument: f(a, xs..., b) is fine.

There are no keyword arguments yet. There's also no anonymous function (lambda) syntax yet – where you'd reach for one, define a small named fn instead.

Swap arguments

Data in Raven is generally immutable. To "modify" a list, you make a new one and assign it back to the same variable:

xs = [1, 2, 3]
xs = append(xs, 4)

The swap operator & makes this pattern convenient. It appears in both definitions and calls:

fn inc(&x) {
x = x + 1
}

x = 5
inc(&x)
show x # x = 6

This resembles taking an address in languages like C, but it's really just shorthand for x = inc(x). When you use &x in a signature, x will be returned back to the caller; when you use &foo at a call site, foo is updated with that returned value. Both sides opt in, so a variable is only ever changed when it's marked with & – functions can only make changes with permission.

Here's a function that switches two variables:

fn switch(&a, &b) {
[a, b] = [b, a]
return
}

a = 5, b = "foo"
switch(&a, &b)
show a # a = "foo"
show b # b = 5

When we talk about variables "changing", it's in the sense of changing clothes. A change to one variable can never affect another:

xs = [1, 2, 3]
ys = xs
append(&ys, 4)
show xs # xs = [1, 2, 3]
show ys # ys = [1, 2, 3, 4]

There's more on this way of thinking in Values & Data.