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.