About this ‘A Language a Day’ Advent Calendar
This series of publications transformed into my book ‘A Language a Day’, which you can get in both electronic and paper format. Languages covered in the book: C++, Clojure, Crystal, D, Dart, Elixir, Factor, Go, Hack, Hy, Io, Julia, Kotlin, Lua, Mercury, Nim, OCaml, Raku, Rust, Scala, and TypeScript.
Welcome to Day 16 of this year’s A Language a Day Advent Calendar. Today’s topic is introduction to the V programming language.
Facts about the language
Some facts about the V programming language:
- Inspired by Go
- Is still under heavy development
- Compiled language
- Statically typed
- Appeared in 2019
- Website: vlang.io
- Searchable term: #vlang
Installing and running V
You can either download a package or install V from sources. In my case, it took a few seconds and went flawless. After that, you will have the v
executable, to which you can point a symlink or update your PATH
variable.
To compile a program, run:
$ v hello-world.v
This command builds an executable file:
$ ./hello-world
Alternatively, run and compile with a single command:
$ v run hello-world.v
Hello, World!
Here is a Hello, World! program in the V language:
println("Hello, World!")
Actually, the Hello, World! example could be re-written with the main()
function:
fn main() { println("Hello, World!") }
Variables
No keywords are required to create a variable in V, but you need the :=
operator do declare and initialise it. Variables cannot be global, so you need a function to create a variable:
fn main() { name := "John" println("Hello, $name!") }
To change the value of a variable, use the assignment operator =
, but make sure to declare the variable as mut
:
fn main() { mut age := 21 println("Their age was ${age}.") age = 22 println("Their age is ${age} now.") }
In the above examples you’ve seen how the variable is interpolated in a string: "$name"
. In the second example, curly braces were added so that the compiler does not treat the following dot as a method call: "${age}."
.
Functions
Functions are declared with the fn
keyword. We have already seen the use of the main
function, which does not take any arguments and does not return a value.
Functions can take parameters and return values; it is also possible to return comma-separated multiple values. You should add type annotations to both the parameters and the result.
fn main() { println(greet("Alla")) } fn greet(name string) string { return 'Hello, ' + name + '!' }
Note that you can declare a function after you use it in the code.
In this example, string concatenation was done with +
, and the strings were surrounded with single quotes unlike the earlier examples. The interpolations with a $
work both with single- and double-quoted strings.
A Factorial example
Let us create a recursive function for computing factorials:
fn factorial(n int) int { if n < 1 {return 1} else {return n * factorial(n - 1)} } fn main() { println(factorial(1)) // 1 println(factorial(5)) // 120 }
Structs and methods
There are no classes in V, so let us look at structures and how to create functions operating them.
Let us write code for keeping personal data. First, the data structure:
struct Person { name string age int }
You can now create an object of this type:
p := Person{name: "John", age: 22}
Notice that there are no keywords involved here at all (if we don’t count the :=
sequence).
To define a method for this data type, create a function that takes a receiver as a special kind of arguments:
fn (p Person) info(delta int) string { return "In $delta years, $p.name will be ${p.age+delta} years old." }
Here, the (p Person)
construct indicates that you can call the method on a Person
object using the dot:
fn main() { p := Person{name: "John", age: 22} println(p.info(5)) }
The method can take other arguments as any function does.
Overloading methods
It is not possible to overload a function based on the arguments. The following code will not compile:
struct Cat {} struct Dog {} fn f(x Dog) { println("Dog") }fn f(x Cat) {println("Cat") }
The compiler sees the second f
as an attempt to redefine the function. But you can use the receiver to dispatch function calls as it is demonstrated in the next example.
A Polymorphic example
Here is the solution of the zoo task. The two structures are independent, as you don’t need to express hierarchy.
struct Cat {} struct Dog {} fn (x Dog) info() string { return "Dog" } fn (x Cat) info() string { return "Cat" } zoo1 := [Cat{}, Cat{}] zoo2 := [Dog{}, Dog{}] for a in zoo1 { println(a.info()) } for a in zoo2 { println(a.info()) }
You might have noticed that although the we are using the same code a.info()
to invoke a method for both cats and dogs, there are two arrays that keep them separately. In V, all array elements should be of the same type.
Interfaces
Let us modify the zoo program to introduce interfaces. First, here is the interface itself. The name of the interface must end with er
as of today:
interface Informer { info() string }
Second, the function that accepts the argument of the Informer
type:
fn zoo_info(i Informer) string { return i.info() }
Having the interface defined, you can call the zoo_info
function and pass the objects of different types to it:
for a in zoo1 { println(zoo_info(a)) // a is a Cat } for a in zoo2 { println(zoo_info(a)) // a is a Dog }
Concurrency
The V language offers the go
system thread launcher at the moment, and it looks like more concurrency support will be soon implemented.
Sleep sort
In the below implementation, the sort_me
function is launched in a separate thread for each data item.
import time fn sort_me(n int) { time.sleep_ms(n * 20) println(n) } fn main() { data := [10, 4, 2, 6, 2, 7, 1, 3] for n in data { go sort_me(n) } time.sleep_ms(1000) }
To join the threads, a big enough delay is added at the end of the program. Examine the example in the project repository to get an idea of how to make this better using wait()
.
Hot code reloading
V’s fast compilation and execution allows them to implement the so-called hot code reloading, where you can modify the source code, and it will be immediately recompiled and executed. Here is a slightly modified example from the documentation page:
module main import time import os [live] fn print_message() { println('Hello! Modify this message while the program is running.') } fn main() { mut counter := 1 for { print_message() println('Run #$counter') counter++ time.sleep_ms(500) } }
The [live]
attribute allows you to change the marked function while the program is running. Run it with using the following command line:
$ v -live run hot-reload.v
The program prints the message in an infinite loop, but you can change the text in the print_message
function and see the result without recompiling the program.
Get more
This time, I can recommend you not only to read additional materials about the language, but also to do it again in a few months, as the language is still under development (but already demonstrates excellent result in both the design and the implementation).
You will find the source files for today’s article on GitHub.
Next: Day 17. Go