Functions 2
Pure functions by default
V functions are pure by default, meaning that their return values are a function of their arguments only, and their evaluation has no side effects (besides I/O).
This is achieved by a lack of global variables and all function arguments being immutable by default, even when references are passed.
V is not a purely functional language however.
There is a compiler flag to enable global variables (--enable-globals
), but this is intended for low-level applications like kernels and drivers.
Mutable arguments
It is possible to modify function arguments by using the keyword mut
:
struct User {
name string
mut:
is_registered bool
}
fn (mut u User) register() {
u.is_registered = true
}
mut user := User{}
println(user.is_registered) // "false"
user.register()
println(user.is_registered) // "true"
In this example, the receiver (which is simply the first argument) is marked as mutable, so register()
can change the user object. The same works with non-receiver arguments:
fn multiply_by_2(mut arr []int) {
for i in 0 .. arr.len {
arr[i] *= 2
}
}
mut nums := [1, 2, 3]
multiply_by_2(mut nums)
println(nums)
// "[2, 4, 6]"
Note, that you have to add mut
before nums
when calling this function. This makes it clear that the function being called will modify the value.
It is preferable to return values instead of modifying arguments. Modifying arguments should only be done in performance-critical parts of your application to reduce allocations and copying.
For this reason V doesn’t allow the modification of arguments with primitive types (e.g. integers). Only more complex types such as arrays and maps may be modified.
Use user.register()
or user = register(user)
instead of register(mut user)
.
V makes it easy to return a modified version of an object:
struct User {
name string
age int
is_registered bool
}
fn register(u User) User {
return {
u |
is_registered: true
}
}
mut user := User{
name: 'abc'
age: 23
}
user = register(user)
println(user)
Anonymous & high order functions
fn sqr(n int) int {
return n * n
}
fn run(value int, op fn (int) int) int {
return op(value)
}
fn main() {
println(run(5, sqr)) // "25"
// Anonymous functions can be declared inside other functions:
double_fn := fn (n int) int {
return n + n
}
println(run(5, double_fn)) // "10"
// Functions can be passed around without assigning them to variables:
res := run(5, fn (n int) int {
return n + n
})
}