🪄 V Philosophy & Introduction
🎯 What is V?
V (Vlang) is a statically typed, compiled programming language created by Alexander Medvednikov. V aims for simplicity, performance, and safety. It compiles to native code via C, producing fast executables with zero dependencies. V features a clean syntax similar to Go, but with memory safety (no null, no global variables), fast compilation (100k lines/sec), and built-in GUI library.
🔬 Core Principles
- No null: All values are initialized, optional types for nullable values.
- No globals: Encourages functional purity and testability.
- No undefined behavior: Array bounds checking, no pointer arithmetic.
- Fast compilation: Compiles millions of lines per second.
- Autofree: Automatic memory management without GC overhead.
- Built-in GUI: Native UI framework (UI, graphics, web).
// V: Simplicity meets performance
module main
fn main() {
println("Hello, Vlang!")
// No null, no globals
name := "V"
println("Language: $name")
// Fast compilation and clean syntax
numbers := [1, 2, 3, 4, 5]
for n in numbers {
println(n)
}
}📦 Variables & Type System
🎯 Immutability by Default
Variables are immutable by default (:=). Use mut for mutable variables. Strong static typing with type inference. Primitive types: int, i8-u64, f32, f64, byte, bool, string.
module main
fn main() {
// Immutable by default
name := "Vlang"
// name = "Other" // Error: cannot assign to immutable
// Mutable variables
mut count := 0
count++
// Explicit typing
age: int = 25
pi: f64 = 3.14159
is_active: bool = true
// Constants
const version = "0.4.0"
const max_size = 1000
// Type aliases
type UserId = int
uid := UserId(12345)
// Option type (nullable)
mut maybe_name: ?string = none
maybe_name = "Alice"
if name := maybe_name {
println(name)
}
// String interpolation
println("$name is $age years old")
}⚙️ Functions & Methods
🎯 First-Class Functions
Functions are first-class citizens. Support for multiple returns, named returns, variadic parameters, and closures.
module main
// Basic function
fn add(x int, y int) int {
return x + y
}
// Multiple returns
fn divide(a, b int) (int, string) {
if b == 0 {
return 0, "division by zero"
}
return a / b, ""
}
// Named returns
fn multiply(x, y int) (result int) {
result = x * y
return
}
// Variadic function
fn sum(numbers ...int) int {
mut total := 0
for n in numbers {
total += n
}
return total
}
// Closure
fn make_adder(x int) fn(int) int {
return fn (y int) int {
return x + y
}
}
// Method on struct
struct Counter {
mut:
value int
}
fn (mut c Counter) inc() {
c.value++
}
fn (c Counter) get() int {
return c.value
}
fn main() {
println(add(5, 3))
q, err := divide(10, 2)
println("$q, $err")
adder := make_adder(10)
println(adder(5)) // 15
mut c := Counter{}
c.inc()
c.inc()
println(c.get()) // 2
}🔀 Control Flow Mastery
🎯 Conditionals & Pattern Matching
if/else with conditions, match for pattern matching (like switch on steroids), and or for option handling.
module main
fn main() {
score := 85
// If-else
if score >= 90 {
println("A+")
} else if score >= 80 {
println("B")
} else {
println("C")
}
// If as expression
status := if score >= 60 { "pass" } else { "fail" }
println(status)
// Match (pattern matching)
day := "Monday"
match day {
"Monday", "Tuesday", "Wednesday", "Thursday", "Friday" {
println("Weekday")
}
"Saturday", "Sunday" {
println("Weekend")
}
else {
println("Invalid")
}
}
// Match with ranges
number := 42
match true {
number < 0 { println("negative") }
number >= 0 && number <= 100 { println("between 0-100") }
else { println("large") }
}
// Option handling with or
mut maybe_val: ?int = none
val := maybe_val or { 0 } // Default if none
println(val)
}🔄 Loops & Iteration
🎯 For Loop Versatility
V has a single for loop that can be used as range loop, while loop, and infinite loop.
module main
fn main() {
// Range loop
for i in 0..5 {
println(i) // 0,1,2,3,4
}
// Range with step
for i in 0..10 step 2 {
println(i) // 0,2,4,6,8
}
// While-style
mut i := 0
for i < 5 {
println(i)
i++
}
// Infinite loop with break
mut count := 0
for {
count++
if count > 3 {
break
}
println(count)
}
// Iterate over array
names := ["Alice", "Bob", "Charlie"]
for name in names {
println(name)
}
// With index
for index, name in names {
println("$index: $name")
}
// Continue and labels
outer: for i in 0..3 {
for j in 0..3 {
if i == j {
continue outer
}
println("$i, $j")
}
}
}📊 Arrays & Maps
🎯 Dynamic Collections
Arrays are dynamic, bounds-checked, and support slicing. Maps are hash tables with any key type.
module main
fn main() {
// Arrays
mut numbers := [1, 2, 3, 4, 5]
numbers << 6 // Append
numbers << [7, 8, 9] // Append multiple
println(numbers) // [1,2,3,4,5,6,7,8,9]
println(numbers.len)
// Fixed-size array
fixed: [5]int = [1,2,3,4,5]
// Multi-dimensional
matrix := [[1,2,3], [4,5,6], [7,8,9]]
// Array methods
println(numbers[1..4]) // Slice
numbers.delete(2) // Remove at index
numbers.reverse()
numbers.sort()
// Array initialization
zeros := [0].repeat(10) // [0,0,0,0,0,0,0,0,0,0]
// Maps
mut ages := map[string]int{
"Alice": 30
"Bob": 25
}
ages["Charlie"] = 35
ages.delete("Bob")
for name, age in ages {
println("$name: $age")
}
// Check existence
if val := ages["Alice"] {
println("Alice is $val")
} else {
println("Not found")
}
}🏗️ Structs & Interfaces
🎯 Object-Oriented without Inheritance
Structs with methods, embedding (composition over inheritance), and interfaces for polymorphism.
module main
// Struct definition
struct Person {
name string
age int
mut:
email string // Mutable field
}
// Methods
fn (p Person) greet() string {
return "Hello, I'm ${p.name}"
}
fn (mut p Person) set_email(email string) {
p.email = email
}
// Struct embedding (composition)
struct Employee {
Person // Embedding
position string
}
// Interface
interface Speaker {
speak() string
}
struct Dog {
name string
}
fn (d Dog) speak() string {
return "Woof!"
}
struct Cat {
name string
}
fn (c Cat) speak() string {
return "Meow!"
}
fn make_sound(s Speaker) {
println(s.speak())
}
fn main() {
mut p := Person{
name: "Alice"
age: 30
email: "alice@example.com"
}
println(p.greet())
p.set_email("new@example.com")
// Embedded struct
e := Employee{
Person: Person{
name: "Bob"
age: 25
email: "bob@work.com"
}
position: "Developer"
}
println(e.greet())
// Interface
dog := Dog{"Rex"}
cat := Cat{"Whiskers"}
make_sound(dog)
make_sound(cat)
}🛡️ Error Handling (Option/Result)
🎯 No Exceptions, Just Values
V uses Option (nullable) and Result types for error handling. No try/catch, just values.
module main
import os
// Function returning Result
fn safe_divide(a int, b int) ?int {
if b == 0 {
return none // Option: none
}
return a / b
}
// Function with custom error
fn read_file(path string) !string {
content := os.read_file(path) or {
return error("Failed to read: $path")
}
return content
}
// Custom error type
struct MyError {
code int
msg string
}
fn risky_operation(x int) !int {
if x < 0 {
return MyError{code: 400, msg: "negative not allowed"}
}
return x * 2
}
fn main() {
// Option handling
result := safe_divide(10, 2)
if val := result {
println("Result: $val")
} else {
println("Division by zero")
}
// Using or with default
val := safe_divide(10, 0) or { 0 }
println(val) // 0
// Result propagation
content := read_file("test.txt") or {
println("Error: $err")
return
}
println(content)
// Match on result
match risky_operation(5) {
.ok(value) { println("Success: $value") }
.err(err) { println("Error: $err") }
}
}⚡ Concurrency (Spawning)
🎯 Lightweight Threads
V uses spawn for concurrent execution. Channels for communication between threads.
module main
import sync
fn worker(id int, ch chan int) {
println("Worker $id started")
result := id * id
ch <- result // Send result
}
fn main() {
// Channels
ch := chan int{cap: 10}
// Spawn concurrent tasks
for i in 1..5 {
spawn worker(i, ch)
}
// Receive results
for _ in 1..5 {
result := <-ch
println("Got: $result")
}
ch.close()
// Wait groups
mut wg := sync.new_waitgroup()
for i in 1..3 {
wg.add(1)
spawn fn (id int, wg &sync.WaitGroup) {
defer { wg.done() }
println("Task $id complete")
}(i, &wg)
}
wg.wait()
println("All done!")
// Shared memory with mutex
mut m := sync.new_mutex()
mut counter := 0
for _ in 0..100 {
spawn fn (mut m sync.Mutex, mut counter int) {
m.lock()
counter++
m.unlock()
}(mut m, mut counter)
}
}💾 Memory Management (Autofree)
🎯 Automatic without GC
V uses autofree - automatic memory management at compile time. Optional GC for complex cases.
module main
import strings
// Struct with manual cleanup
struct FileHandle {
fd int
}
fn (f &FileHandle) close() {
// Manual cleanup if needed
println("Closing file descriptor ${f.fd}")
}
fn main() {
// Automatic memory management
mut builder := strings.new_builder(100)
builder.write_string("Hello")
builder.write_string(" ")
builder.write_string("V!")
result := builder.str()
println(result)
// Defer for cleanup
{
f := &FileHandle{fd: 42}
defer { f.close() }
println("Using file")
} // defer runs here
// Reference counting with shared
shared := &strings.Builder{}
shared.write_string("Shared data")
// No manual free needed - autofree handles it
// For performance-critical code, use -autofree flag
}📦 Modules & Imports
🎯 Simple Module System
Modules organize code. VPM (V Package Manager) for dependencies.
// File: mymodule.v
module mymodule
pub fn greet(name string) string {
return "Hello, $name!"
}
pub struct User {
pub name string
age int // private by default
}
// File: main.v
module main
import mymodule // Import custom module
import os // Standard library
import net.http // HTTP client
fn main() {
// Using imported module
msg := mymodule.greet("Vlang")
println(msg)
user := mymodule.User{
name: "Alice"
}
println(user.name)
// Standard library
println(os.getwd())
// HTTP request
resp := http.get("https://example.com") or {
println("Error: $err")
return
}
println(resp.status_code)
// Import with alias
import time as t
println(t.now())
}🔧 Generics & Sum Types
🎯 Type-Parametric Programming
Generics for type-safe containers. Sum types for algebraic data types.
module main
// Generic struct
struct Stack {
mut:
items []T
}
fn (mut s Stack) push(item T) {
s.items << item
}
fn (mut s Stack) pop() ?T {
if s.items.len == 0 {
return none
}
return s.items.pop()
}
// Generic function
fn max(a T, b T) T {
return if a > b { a } else { b }
}
// Sum type (algebraic data type)
type Expr = IntExpr | AddExpr | SubExpr
struct IntExpr {
value int
}
struct AddExpr {
left Expr
right Expr
}
struct SubExpr {
left Expr
right Expr
}
fn evaluate(expr Expr) int {
return match expr {
IntExpr { expr.value }
AddExpr { evaluate(expr.left) + evaluate(expr.right) }
SubExpr { evaluate(expr.left) - evaluate(expr.right) }
}
}
fn main() {
// Generic stack
mut int_stack := Stack{}
int_stack.push(10)
int_stack.push(20)
println(int_stack.pop())
mut str_stack := Stack{}
str_stack.push("Hello")
str_stack.push("V")
// Generic function
println(max(10, 20))
println(max(3.14, 2.71))
// Sum type
expr := AddExpr{
left: IntExpr{5}
right: SubExpr{
left: IntExpr{10}
right: IntExpr{3}
}
}
println(evaluate(expr)) // 5 + (10 - 3) = 12
} 🔌 C Interop & Hot Reload
🎯 Seamless C Integration
Call C libraries directly. Hot code reload for rapid development.
module main
// Import C standard library
#flag -lm
#include
fn C.sqrt(x f64) f64
// Import custom C function
#flag -lSDL2
#include
fn C.SDL_Init(flags u32) int
fn C.SDL_Quit()
// C structs
struct C.SDL_Window
fn C.SDL_CreateWindow(title &u8, x int, y int, w int, h int, flags u32) &C.SDL_Window
// V function calling C
fn main() {
// Math library
println(C.sqrt(144.0))
// SDL example
if C.SDL_Init(0x20) != 0 {
println("SDL init failed")
return
}
defer { C.SDL_Quit() }
println("SDL initialized")
// Inline C
$if !js {
$emit("printf(\"Inline C works!\\n\");")
}
}
// Hot reload example (live coding)
[live]
fn live_update() {
println("This function can be updated at runtime!")
}
// Use hot reload flag: v -live main.v
fn main_live() {
for {
live_update()
time.sleep(1 * time.second)
}
} 🌐 Web & GUI Development
🎯 Full-Stack Capabilities
Built-in web server, UI framework, and cross-platform GUI development.
module main
import vweb
import ui
// Web server example
struct App {
vweb.Context
}
fn (mut app App) index() vweb.Result {
return app.html("Hello from V!
")
}
fn (mut app App) user(id string) vweb.Result {
return app.text("User: $id")
}
fn web_server() {
mut app := &App{}
vweb.run(app, 8080)
}
// GUI example
struct Window {
ui.Window
mut:
counter int
}
fn (mut window Window) on_button_click() {
window.counter++
window.ui.label(1).set_text("Clicked: ${window.counter}")
}
fn gui_example() {
window := ui.window(
width: 400
height: 300
title: "V GUI"
children: [
ui.row(
children: [
ui.button(
text: "Click me"
onclick: fn (b &ui.Button) {
println("Button clicked!")
}
),
ui.label(text: "Hello V UI")
]
)
]
)
ui.run(window)
}
// HTTP client
import net.http
fn fetch_data() {
resp := http.get("https://api.github.com") or {
println("Error: $err")
return
}
println(resp.text)
}
fn main() {
println("V for web and GUI")
// Uncomment to run:
// go web_server()
// go gui_example()
}