Sintaxis de V para quien viene de Go: variables, tipos, structs y funciones

V es un lenguaje compilado que apunta directamente a la sintaxis de Go: gramática mínima, tipado fuerte con inferencia, cero herencia clásica y una filosofía de «una sola forma de hacer las cosas». Si vienes de Go te vas a sentir cómodo casi de inmediato, aunque hay diferencias importantes en cómo V trata la mutabilidad, los structs y las funciones. Antes de entrar en detalle: V sigue en beta (versión 0.5.1 en el momento de escribir esto), así que trátalo como lo que es, un lenguaje interesante para experimentar y no todavía una opción para producción crítica.

Variables: inmutables por defecto

V solo tiene una forma de declarar variables, con :=, y las hace inmutables salvo que digas lo contrario con mut:

name := 'Bob'
age := 20
large_number := i64(9999999999)

mut counter := 0
counter = 1 // esto falla si counter no lleva `mut`

mut a := 0
mut b := 1
a, b = b, a // swap sin variable temporal

La diferencia con Go es sutil pero se nota en la práctica: en Go declaras con var o := y la mutabilidad es implícita, en V tienes que pedir permiso explícito con mut cada vez que quieras reasignar. Es una decisión de diseño pensada para que el compilador (y quien lee el código) sepa de un vistazo qué variables cambian.

Tipos básicos

V trae los tipos numéricos habituales (i8, i16, int, i64, u8... f32, f64), strings UTF-8 inmutables con interpolación, arrays y mapas con sintaxis muy próxima a Go:

// Números
b := i64(123)
hex := 0x7B
bin := 0b01111011
big := 1_000_000 // separador de miles

// Strings e interpolación
name := 'Bob'
println('Hello, ${name}!')
raw := r'hellonworld' // string crudo, sin procesar escapes

// Arrays
mut nums := [10, 20, 30]
nums << 40
even := nums.filter(it % 2 == 0)
fixed := [3]int{} // array de tamaño fijo

// Mapas
mut m := map[string]int{}
m['one'] = 1
val := m['key'] or { 0 } // valor por defecto si la clave no existe

El detalle que más llama la atención viniendo de Go es el manejo de claves ausentes en mapas: en vez de comprobar un segundo valor booleano (v, ok := m[key]), V usa un bloque or que también sirve para gestionar errores en toda la librería estándar, como veremos en el próximo artículo de la serie.

Funciones: sin sobrecarga, con retorno múltiple

fn add(x int, y int) int {
    return x + y
}

fn foo() (int, int) {
    return 2, 3
}

a, b := foo()
c, _ := foo() // ignorar un valor con `_`

// Firma multilínea: las comas entre parámetros son opcionales
fn greet(
    salutation string
    name string
) string {
    return 'Hey, ${salutation} ${name}!'
}

V no permite sobrecarga de funciones, igual que Go, y sí soporta hoisting: puedes llamar a una función antes de declararla en el mismo fichero, algo que en Go también funciona pero que en V se documenta explícitamente como parte del diseño del lenguaje.

Structs: campos privados por defecto

Aquí es donde V se aleja más de Go. Los campos de un struct son privados e inmutables salvo que los marques con mut: o pub::

struct User {
    a int          // privado e inmutable (por defecto)
    mut: b int     // privado y mutable
    pub: d int     // público e inmutable
    pub mut: e int // público y mutable desde fuera del módulo
    age int
}

// Inicialización con nombres de campo
p := User{ age: 20 }

// Inicialización posicional
point := Point{10, 20}

// Métodos: el receptor va entre `fn` y el nombre del método
fn (u User) can_register() bool {
    return u.age > 16
}

user.can_register()

Go resuelve la visibilidad a nivel de paquete con mayúsculas/minúsculas en el nombre del campo; V lo hace campo por campo con estos cuatro niveles, lo que da más control pero obliga a pensarlo desde el primer momento en que diseñas el struct. También soporta embedding de structs, con herencia automática de campos y métodos, similar a la composición de Go pero con azúcar sintáctico añadido para acceder a los campos embebidos directamente.

Con esto ya tienes lo básico para leer código V sin sorpresas. En el próximo artículo de la serie entramos en el terreno más discutido del lenguaje: cómo gestiona la memoria de verdad (que no es exactamente lo que promete la web oficial). Si quieres el contexto completo sobre el estado actual del proyecto, lo hemos cubierto en esta noticia sobre la versión 0.5.1. Y si te interesa ver cómo Go resuelve genéricos y patrones funcionales equivalentes, tenemos este artículo sobre generics en Go en producción.

Imagen: Pexels / Markus Spiske

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP