I’ve known about V for quite a few years, and I admit I had stopped following the language’s development for some time. It’s a language that promises a lot, almost seeming too good to be true. The simplicity of a dynamic language, with a Go-like syntax (but better), yet with performance comparable to C/C++/Rust while maintaining good compilation speed.

println('hello world!')

It’s hard to get simpler than that. In a real program, however, we’ll have the classic main entry point:

fn main () {
    println('hello world!')
}

The overall syntax of V is very similar to Go’s, so it’s said that 80% of Go can be found in V. Personally, I’m not a fan of Go’s syntax, but that’s purely aesthetic, and we must admit, this simplicity is damn effective and constitutes a strength of the language compared to the complexity of Rust.

Speaking of Rust, V had the good idea of borrowing the functional style of the language, which means we can write Rust-like code like this with the famous match to decompose an enum:

enum Color {
    red
    blue
    green
}

fn is_red_or_blue(c Color) bool {
    return match c {
        .red, .blue { true } // comma can be used to test multiple values
        .green { false }
    }
}

Or the traditional map/filter that I’ve known since Node.js and can’t live without, which I miss so much when I’m doing Go:

// using filter, map and negatives array slices
files := ['pippo.jpg', '01.bmp', '.v.txt', 'img_02.jpg', 'img_01.JPG']
filtered := files.filter(it[-4..].to_lower() == '.jpg').map(it.to_upper())
// ['PIPPO.JPG', 'IMG_02.JPG', 'IMG_01.JPG']

Another good idea borrowed from Rust that appears in this language is the Option/Result type.

struct User {
    id   int
    name string
}

struct Repo {
    users []User
}

fn (r Repo) find_user_by_id(id int) !User {
    for user in r.users {
        if user.id == id {
            // V automatically wraps this into a result or option type
            return user
        }
    }
    return error('User ${id} not found')
}

// A version of the function using an option
fn (r Repo) find_user_by_id2(id int) ?User {
    for user in r.users {
        if user.id == id {
            return user
        }
    }
    return none
}

fn main() {
    repo := Repo{
        users: [User{1, 'Andrew'}, User{2, 'Bob'}, User{10, 'Charles'}]
    }
    user := repo.find_user_by_id(10) or { // Option/Result types must be handled by `or` block
        println(err)
        return
    }
    println(user.id)   // "10"
    println(user.name) // "Charles"

    user2 := repo.find_user_by_id2(10) or { return }

    // To create an Option var directly:
    my_optional_int := ?int(none)
    my_optional_string := ?string(none)
    my_optional_user := ?User(none)
}

Those who have done Rust should not be lost: ! for the result type and ? for the option type.

If the function returns a result, it returns an error type, while if it returns an option, it returns a none. And when calling a function that returns these types, we must obligatorily add an or {} block.

Personally, I find it super elegant.

We also have other concepts like high-order functions and closures; functions are therefore first-class citizens in this language and can be treated as variables.

my_int := 1
my_closure := fn [my_int] () {
    println(my_int)
}
my_closure() // prints 1

The language is transpiled to C, and the produced code is human-readable for those who do C. So V’s promise is enormous, I think, to have this clear and simple syntax and still have C-like performance. I’m going to start following this language closely again and do some experiments. To test V, it’s here