Greboca  

Suport technique et veille technologique

Aujourd’hui, les grandes entreprises et administrations publiques hésitent entre continuer à utiliser des logiciels propriétaires ou basculer vers les Logiciels Libres. Pourtant, la plupart des logiciels libres sont capables de bien traiter les données issues des logiciels propriétaire, et parfois avec une meilleur compatibilité.

C’est alors la barrière de la prise en main qui fait peur, et pourtant...

Les logiciels libres

L’aspect « Logiciel Libre » permet une évolution rapide et une plus grande participation des utilisateurs. Les aides et tutoriels foisonnent sur Internet ou sont directement inclus dans le logiciel lui-même.

Enfin, les concepteurs sont plus proches des utilisateurs, ce qui rend les logiciels libres plus agréable à utiliser et conviviaux.

Grâce à la disponibilité des logiciels libres, vous trouverez facilement des services de support techniques et la licence n’est plus un frein à l’utilisation de ces logiciels par votre personnel.

Notre support technique concerne essentiellement les logiciels libres, que ce soit sous forme de services ponctuels ou de tutoriels.

DLFP - Dépêches  -  À la découverte du langage V

 -  Septembre 2023 - 

V est un langage récent (première version libre sortie en 2019) développé initialement par Alex Medvednikov pour ses propres besoins sur le logiciel volt.

Dans cette dépêche, j'aimerais vous le faire découvrir, et, je l'espère, vous donner le goût d'en découvrir d'avantage.

le logo du langage V

Sommaire

Introduction

J’étais tombé, il y a quelque temps, sur un article sur developpez.com annonçant que le langage V venait d’être Open source. À l’époque, j’avais simplement été étonné par la légèreté et la rapidité annoncée du langage, tout en voyant qu’à peine sorti il était déjà beaucoup critiqué, nous y reviendrons.

Puis, je ne sais pas trop pourquoi, un jour je me suis rappelé de ce langage qui semblait vouloir tout réinventer, jusqu’à pouvoir même se passer de la libc.

Piqué par la curiosité, j’ai décidé de lui donner une chance. Comme commençait l’excellent « calendrier de l’avent du code », que je venais de découvrir, je me suis dit que j’allais le réaliser avec V.

Les puzzles des différents jours de ce challenge serviront d’exemple. Notez qu’ils ne se suffisent généralement pas à eux-mêmes et ne compileront pas. J’ai cependant préféré mettre des extraits de code réel que des bouts de code de démonstration. Autant que possible, j’ai mis un lien vers le code utilisé.

Historique

C’est, à la base, une sorte de « clone de go » qui génère du C (compilé ensuite en langage machine par un compilateur type tcc ou gcc, voir cette section pour plus de détail) qui a vite évolué pour devenir un langage à part entière.

Il tente de faire le grand écart entre la facilité d’utilisation d’un langage « haut niveau » tels que Python ou JavaScript et des performances que l’on retrouve généralement avec des langages plus « bas niveau » tels que C/C++ ou Rust. Après plusieurs versions alpha, le langage est aujourd’hui en phase de bêta avec une v0.4 sortie récemment (la 0.4.1 vient tout juste de sortir).

L’objectif étant, comme pour Go, qu’au moment de la sortie de la v1.0 le langage soit considéré comme stable et assure une rétro compatibilité de tous programme écrit depuis la v1.0.

Ah, j’allais oublier le plus important : V a une sympathique mascotte depuis quelques années !

Mascotte de V

Rapide tour d’horizon

Sans vouloir être aussi exhaustif que la doc officielle ou ce superbe guide, parcourons ensemble les éléments important du langage.

La syntaxe de V est quasiment identique à celle de Go avec quelques emprunts à Rust.
L’objectif étant de ne pas réinventer la roue, mais — justement — permettre aux développeurs d’appréhender ce langage le plus rapidement et sans surprises.

C’est une syntaxe de type « C » (avec les accolades), mais très épurée. Je crois que c’est une de mes syntaxes préférée. Très lisible avec peu de mots clés.

V n’est pas une révolution, mais s’inspire de beaucoup d’autres langages pour en prendre les meilleurs concepts et les intégrer dans une syntaxe à la fois accessible et pragmatique. Ce qui le fait s’écarter de la « pureté » de langage comme Zig ou Rust, qui préfèrent avoir une syntaxe souvent plus verbeuse, mais plus exacte. En résulte un langage aussi expressif que Python, Ruby ou Javascript, mais avec une compilation avant l’exécution (ce n’est pas un langage interprété), un typage fort et des performances proches du C.

Installation

Le langage s’installe facilement. La méthode la plus simple (peut-être la seule actuellement ?) est de cloner le dépôt git :

git clone https://github.com/vlang/v
cd v
make

Le tout compile en quelques secondes grâce à un binaire tcc téléchargé pendant le make.

Il n’y a même pas de cible install dans le makefile, juste un petit argument symlink au binaire pour créer un lien symbolique vers le dossier des binaires de votre système (/usr/local/bin/v sur les systèmes Unix).

sudo ./v symlink

Compiler et exécuter du code

La commande run permet de compiler (générer un code C puis le compiler, voir plus bas) et exécuter du code contenu dans un fichier.

Exemple :

v run hello_world.v

Hello World

Le traditionnel « Hello world » peut s’écrire de manière simplifiée :

println("Hello world")

La fonction main n’étant pas obligatoire dans les petits programmes.

La version plus verbeuse serait :

fn main() {
    println("Hello world")
}

Des variables immuables par défaut

Les variables dans V sont immuables par défaut. C’est un choix fort du langage qui s’inspire ici des langages fonctionnels.

Autre choix important : il n’est pas possible de déclarer des variables en dehors d’une fonction, seules les constantes peuvent l’être (comme en Rust).

La déclaration de variables se fait de manière similaire à Go, avec un opérateur := qui se distingue de l’opérateur d’affectation =.

Cette différence d’opérateur peut rebuter au premier abord, mais elle permet de ne pas accidentellement affecter une variable existante quand on voulait en créer une.

À noter que, contrairement à Rust, le shadowing (redéfinition d’une variable portant le même nom) n’est pas autorisé.

Le typage n’est pas nécessaire pour les types de base.

fn play_with_variables() {
    // Nombre immuable en int32 par defaut
    a := 5
    // Interdit
    a = 4
    // unsigned 64 mutable
    mut departure_values := u64(1)
    // autorisé
    departure_values = 2
    // string
    input := '8,13,1,0,18,9'
}

// les constantes se déclarent en dehors des fonctions avec une instruction
// spéciale et peuvent être utilisées dans tout le module et exportée
const (
    n  = 30000000
    k  = 6
    i0 = k - 1
)

Des fonctions typées avec plusieurs retours possibles

La déclaration de fonctions est quasi identique à celle de Go, avec une petite nuance, le mot clé fn est utilisé au lieu de func (comme dans Rust).

Les types de paramètres sont obligatoires et positionnés à droite du nom (contrairement aux autres langages de type « C »).

fn parse_ticket(ticket_str string) []int {
    return ticket_str.split(',').map(it.int())
}
// Il est possible de renvoyer plusieurs valeurs
fn get_pos(str_pos string) (int, int, int) {
    pos := str_pos.split(',').map(it.int())
    return pos[0], pos[1], pos[2]
}
x_pos, y_pos, z_pos := get_pos(str_pos)

À noter que, pour l’instant, il n’est pas possible de définir des valeurs par défaut pour les paramètres, ni de nommer les paramètres lors de l’appel de la fonction (comme en Python). Des discussions sont en cours, mais aucune décision n’a été prise.

Des conditions sans parenthèse

Tout comme en Go, la syntaxe des if/else se fait sans parenthèse (sauf si nécessaire).

// Sans parenthèse
if letter_count >= policy_min && letter_count <= policy_max {
  valid_password_count++
}

// Avec
if (value >= rule[0] && value <= rule[1]) ||
   (value >= rule[2] && value <= rule[3]) {
    valid = true
}

// if/else if
if f.ends_with('cm') {
    return height >= 150 && height <= 193
} else if f.ends_with('in') {
    return height >= 59 && height <= 76
}

Uniquement des boucles for

Comme en Go (et oui syntaxiquement V est très proche de Go), seul le mot clé for permet de faire des boucles.

Il sert à tout : itérer sur des chaînes de caractères, des tableaux ou des maps, boucler un certain nombre de fois, boucler à condition, boucler indéfiniment, etc.

// sur une string
for l in password {
    if l == letter {
        letter_count++
    }
}

// sur des tableaux
answers_group := answers_content.split('\n\n')
answers := answers_group.map(it.replace('\n', ''))
mut yes_count := 0
for group in answers {
    yes_count += remove_duplicates(group).len
}

// sur des map
for index, line in toboggan_map[slope['down']..toboggan_map.len] {
    if index % slope['down'] != 0 {
        continue
    }
    x_pos = (x_pos + slope['right']) % line.len
    if line[x_pos] == `#` {
        tree_encountered++
    }
}

// En itérant entre 2 écarts (la valeur de droite étant exclue)
// "_" n'était pas traité par le compilateur comme une variable non utilisée 
for _ in 0 .. num_cycles {
    grid = run_cycle(grid)
}

// Syntaxe plus classique, parfois utile
for i := i0; i + 2 <= n; i++ {
    // ...
}

// Boucle infinie
for {
    if program[cursor].executed {
        break
    }
    // ...
}

Des octets et des runes

Le type string en V est en fait un simple tableau d’octets immuable.
C’est très performant, mais si on veut travailler sur des chaînes UTF-8 par exemple, c’est un peu problématique étant donné que certains caractères sont codés sur plusieurs octets.

mut s1 := 'toto'
assert s1.len == 4
// Important les string sont immuables
s1[1] = 'a' // Ne compile pas
s2 := '한국/韓國'
assert s2.len == 13 // Et oui c'est la taille en octets

// L'iteration n'aura pas trop de sens non plus...
for b in s2 {
    println(b)
}

Heureusement, V permet aussi de travailler à ce niveau-là grâce à la fonction .runes().

s2 := '한국/韓國'
assert s2.runes().len == 5

for b in s2.runes() {
    println(b)
}

Comme en Go, V distingue les chaînes de caractère des runes via l’utilisation des accents graves.

rocket := `🚀`
assert typeof(rocket) == 'rune'
str := 'launch 🚀'
assert typeof(str) == 'string'

J’apprécie vraiment cette distinction, c’est un confort quand on travaille sur les chaînes de caractère.

Un modèle objet par composition

J’ai longtemps cru au paradigme objet avec tous ses beaux concepts : l’héritage, les accesseurs, le polymorphisme, j’en passe et des meilleurs.
Il faut dire que mes enseignants nous avaient vendu la programmation objet comme l’outil ultime de conception de code (oui l’UML tout ça)…

Comme beaucoup, j’ai déchanté en me cassant les dents avec des arbres d’héritage obscurs et trop complexes, en surchargeant sans m’en rendre compte (certains langages le permettent) des fonctions de la classe mère, en pensant « objet », là où il fallait plutôt penser « données », en écrivant des accesseurs inutiles, etc.

Le seul aspect « intéressant » de la programmation orienté objet est pour moi le fait de pouvoir associer des méthodes à une variable composée (disons une structure). C’est plus élégant et plus lisible.

Je préfère de loin écrire a.push(1) que array_push(a, 1) ou Array.push(a, 1).

V emprunte le même modèle « objet » que Go en permettant d’associer des fonctions à des structures et la composition de structure.

De cette manière on peut créer des structures qui « héritent » du fonctionnement d’autres structures, sans risquer de tomber dans les problèmes liés à la verticalité.

Plusieurs types peuvent partager une même signature en implémentant une interface. Étonnamment, il y a un mot clé pour définir une interface, mais pas pour l’implémenter.

À la différence de Go, les interfaces peuvent contenir des données et pas seulement des méthodes.

Les match un emprunt bien sympa

match est un mot clé visiblement issu du monde de la programmation fonctionnelle. J’ai découvert ce concept pour la première fois avec Elm (appelé case), mais il venait très probablement d'Haskell, Rust aussi le propose.

C’est une sorte de « super switch/case », une syntaxe permettant d’enchaîner des conditions, tout en limitant au maximum le code à écrire.

Comparaison de chaîne, d’entier, inclusion d’une valeur dans un rang alphanumérique, type de la donnée, beaucoup des conditions que l’on pourrait écrire avec un if/else sont plus simples à écrire avec match

À la différence d’un switch/case, il n’est pas nécessaire d’utiliser l’instruction break pour éviter d’aller dans les branches du dessous et comme avec le default de switch/case, on passe forcément dans une des branches avec ici une instruction else obligatoire à la fin.

os := 'windows'
print('V is running on ')
match os {
    'darwin' { println('macOS.') }
    'linux' { println('Linux.') }
    else { println(os) }
}

La programmation parallèle sans effort et sans danger

V a emprunté à Go le principe du fonctionnement du mot clé go pour rendre n’importe quelle fonction parallélisable.
Pour l’instant V crée de vrais processus et non les fameuses « goroutines » qui sont, en quelque sorte, des processus allégés pouvant être créés par milliers. C’est pour ça que le mot clé go a été remplacé par spawn
L’implémentation des « goroutines » est prévue dans les futures versions.

Il emprunte aussi à Go la notion de channel qui simplifie grandement la communication entre différents processus.

import sync
import time

fn task(id int, duration int, mut wg sync.WaitGroup) {
    println('task $id begin')
    time.sleep_ms(duration)
    println('task $id end')
    wg.done()
}

fn main() {
    mut wg := sync.new_waitgroup()
    wg.add(3)
    spawn task(1, 500, mut wg)
    spawn task(2, 900, mut wg)
    spawn task(3, 100, mut wg)
    wg.wait()
    println('done')
}

Les sum types

Les « sommes de types » sont un concept encore emprunté au monde fonctionnel (en tout cas, je les ai aussi découverts avec Elm) qui, aujourd’hui, se démocratise, on les retrouve notamment dans Rust, Zig et TypeScript (et sans doute dans d’autres langages).

L’idée est simple : pouvoir définir des types de données « conditionnels ». Entendez par là, une variable peut avoir « tel ou tel type ».

Là où les structures sont des types de données « additionnels » (une personne a une chaîne de caractère prénom et une chaîne pour son nom), les sommes de type permettent de définir des types de données conditionnelles (exemples tiré de cet article, tous les exemples donnés ne sont pas valables en V) :

  • Un dé peut avoir 6 ou 20 faces
  • Une publication peut avoir un seul auteur (chaîne de caractère) ou plusieurs (tableau de chaîne)
  • Un créateur peut être un artiste ou un auteur en fonction du domaine
  • L’ouverture d’un fichier peut renvoyer le fichier ou une erreur
  • L’accesseur d’un tableau peut renvoyer une donnée ou rien si on est hors du tableau
// Ici la valeur du token peut soit être une string ou un entier non signé sur 64 bits
type TokenValue = string | u64

struct Token {
    kind TokenKind
    val  TokenValue
    loc  int
mut:
    next &Token = 0
}

Une gestion d’erreur moderne

Comme Zig, V intègre la notion d’erreur dans le retour d’une fonction via le caractère ! au début de la déclaration de type ce qui force à gérer l’erreur directement dans la fonction appelante ou à faire remonter l’erreur plus haut.
Cette façon de faire évite d'avoir recours aux exceptions.

// Notez le `!` devant le type de retour pour spécifier que la fonction peut retourner une erreur
fn (r Repo) find_user_by_id(id int) !User {
    for user in r.users {
        if user.id == id {
            return user
        }
    }
    return error('User ${id} not found')
}

fn main() {
    repo := Repo{
        users: [User{1, 'Andrew'}, User{2, 'Bob'}]
    }
    // Le mot clé `or` permet de gérer le cas d'erreur
    user := repo.find_user_by_id(10) or { User{-1, 'Unknown'} }
    println(user)
    // On peut aussi utiliser un if/else
    if user := repo.find_user_by_id(10) {
       println(user)
    } else {
       // Contrairement à Zig qui permet de « capturer » l’erreur via |err| ici la variable `err` est automatiquement créée
       // C’est un choix critiquable, mais je trouve que ça force une convention de code
       println(err)
    }
    // On peut aussi décider de propager l’erreur, dans ce cas le programme s’arrêtera vu qu’on est dans la fonction `main`
    user := repo.find_user_by_id(10)!
}

V intègre aussi la notion de retour optionnel (Option/Result type, concept encore emprunté à la programmation fonctionnelle il me semble) via le caractère ? lui aussi à placer au début de la déclaration de type de retour d’une fonction. Cela permet de spécifier qu’une fonction peut renvoyer un résultat potentiellement vide et permet notamment d’éviter d’avoir des pointeurs null.

// Ici au lieu d’une erreur on renvoie `none`, notez le `?` devant le type de retour
fn (r Repo) find_user_by_id(id int) ?User {
    for user in r.users {
        if user.id == id {
            return user
        }
    }
    return none
}

Ici aussi le compilateur force le développeur à gérer le cas.
Fort heureusement, des sucres syntaxiques bien pratique permettent de gérer facilement les cas où une valeur n’est pas disponible.

Les génériques

À la différence de Go (même si depuis go les a aussi intégrés finalement), V permet l’écriture de code générique.
Pour l’instant, seul un paramètre « template » est pris en compte, mais cette limitation va rapidement être levée.

struct Repo[T] {
    db DB
}

fn new_repo[T](db DB) Repo[T] {
    return Repo[T]{db: db}
}

// This is a generic function. V will generate it for every type it's used with.
fn (r Repo[T]) find_by_id(id int) ?T {
    table_name := T.name // in this example getting the name of the type gives us the table name
    return r.db.query_one[T]('select * from ${table_name} where id = ?', id)
}

db := new_db()
users_repo := new_repo[User](db) // returns Repo[User]
posts_repo := new_repo[Post](db) // returns Repo[Post]
user := users_repo.find_by_id(1)? // find_by_id[User]
post := posts_repo.find_by_id(1)? // find_by_id[Post]

Le fonctionnement des modules

Quelques sucres syntaxiques « magiques »

Des sucres syntaxiques « un peu magiques, mais bien pratiques » pour les fonctions map, filter et sort avec des variables locales automatiquement créées :

a := [1, 2, 3, 4]
b := a.map(it + 1) // it représent l'item courant
println(b) // affiche [2, 3, 4, 5]

mut c := [2, 4, 3, 1]
c.sort(a < b) // a et b réprésente les deux items à comparer
println(c) // affiche [1, 2, 3, 4]

Ces sucres syntaxiques (appelés "compiler magic") sont utilisés à d'autres endroits, notamment dans la bibliothèque SQL (voir plus bas)

Les grandes caractéristiques du langage

Un langage plus transpilé que compilé

La première chose à savoir sur V est que par défaut, il ne fournit pas un véritable compilateur, mais qu’il s’agit plus d’un transpileur vers C. Le code est par la suite compilé via tcc (option par défaut) ou gcc (avec l’argument -prod). Bien sûr, vous pouvez utiliser clang aussi.

Il existe une fonctionnalité, expérimentale pour l’instant, qui permet la compilation sans passer par du C, mais il semble que le C restera l’option par défaut si l’on veut avoir de bonnes performances et une prise en charge d’un maximum de plateformes.

Ce choix de la transpilation peut étonner au premier abord, mais, en réalité je le trouve extrêmement judicieux, il permet :

  • d’avoir de très bonnes performances sans effort vu qu’on bénéficie de dizaines d’années d’optimisation des compilateurs C existants,
  • une compatibilité « gratuite » avec toutes les plateformes existantes permettant un usage aussi bien sur nos ordinateurs, que sur ordiphone ou encore dans l’embarqué (j’ai pu par exemple compiler du V sur Nintendo DS sans trop d’effort),
  • dans le cas de V, une intégration transparente et n’induisant pas un surcoût de performance avec les bibliothèques C existantes.

Bien sûr, d’autres langages y ont pensé, on pourra notamment citer Vala qui transpile une sorte de C# vers C, mais qui est très fortement lié au projet Gnome et ne semble pas avoir décollé en dehors de cet usage.
Il y a aussi nim, dont l’approche est très similaire à V.

La différence entre ces deux langages est résumée sur cette page :

  • nim utilise clang comme compilateur par défaut alors que V se base sur TCC,
  • Le code C généré par nim n’est pas fait pour être lu par des humains alors que V génère un code C plutôt lisible,
  • la syntaxe nim est plus proche de Python que de Go,
  • Nim utilise un ramasse-miette pour gérer la mémoire allouée sur le tas, alors que V vise une approche différente à terme (pour le moment il utilise aussi un ramasse-miette).

À noter que le langage zig s’est récemment doté d’un backend C

On dit du C qu’il est une sorte « d’assembleur glorifié », on peut ainsi dire de V qu’il est une sorte de « C glorifié ».
En ce sens, il se rapproche peut-être plus de bibliothèques comme Cello, tout en poussant le concept plus loin en proposant un vrai compilateur et tout un écosystème qui lui est propre.

Une ABI compatible avec C

Un des très bons choix du langage est d’avoir une ABI compatible avec celle du C.

Cela permet de pouvoir très facilement utiliser des bibliothèques C depuis V sans avoir besoin d’écrire du code de compatibilité (wrapper) entre les deux langages.

C’est un aspect du langage, que l’on retrouve dans zig aussi, que j’apprécie particulièrement, car il ouvre la voie à l’utilisation de tout un tas de bibliothèques fantastiques.

Et cela va dans les deux sens, on peut aussi écrire des modules ou bibliothèques en V utilisables dans un programme C.

Un langage minimaliste

Si on devait décrire V avec un seul mot, ce serait sans doute « minimalisme ».
Cet état d’esprit ne se retrouve pas que dans sa syntaxe, mais aussi dans la limitation des dépendances utilisées par la bibliothèque standard et dans la légèreté du code généré.
On revient aux sources en quelque sorte en limitant les couches d’abstraction (vous me direz, c’est déjà une abstraction du C), tout en ayant une approche moderne.
J’aime vraiment cette idée d’avoir un langage permettant de créer tout un panel d’applications facilement, tout en offrant des performances excellentes, une empreinte mémoire minimale et peu de dépendances.

Un des principes de V est qu’il ne doit y avoir qu’une seule manière de faire les choses.
Par exemple, V ne propose pas une syntaxe spécifique pour les ternaires (comme Go), mais utilise simplement l’instruction if/else, le résultat pouvant être utilisé pour affecter une variable.

max = if current > max { current } else { max }

Un langage typé qui compile vite

C’est un point que V partage avec Go.

Ayant commencé ma carrière professionnelle sur une grosse base de code C++ qui mettait plus de 15min à compiler et ce, malgré une compilation distribuée sur plusieurs machines, je dois avouer que c’est un bonheur de compiler un projet en quelques secondes.

En outre, j’ai passé ces dernières années à coder avec des langages interprétés et faiblement typés (JavaScript, Python, PHP, Ruby, etc.) et il faut avouer que la compilation avant exécution, ça évite beaucoup de soucis. Et puis, on se dit c’est cool de ne rien typer, ça rend le code plus lisible, etc. Mais, quand on en vient à devoir rajouter partout des vérifications de type, des « cast » explicites pour s’assurer d’avoir les bons types, je crois que ça signifie que les types sont nécessaires en programmation.

D’ailleurs, les quelques langages cités plus haut évoluent tous vers des possibilités plus poussée de typage (PHP 7 et 8, JavaScript avec TypeScript, Python > 3.5, etc.)

On a ainsi le meilleur des deux mondes :

  • une exécution quasi instantanée du code,
  • une vérification de l’intégralité du code et pas seulement celui qui est exécuté.

C’est un aspect évident pour tous les développeurs, C/C++, Java, C# ou autres, mais qu’il est bon de rappeler.

Une gestion de la mémoire « basique », mais sans ramasse miette

C’est un aspect que je n’ai pas encore creusé, mais V propose une gestion de la mémoire qui me parait assez « basique ».

Par défaut, les variables de type scalaires et les structures sont allouées sur la pile. Donc, pour ces variables le compilateur n’a rien à faire, leur mémoire est désallouée à la sortie de leur portée.

Les variables de type tableau ou map sont allouées sur le tas.

Il est aussi possible de forcer l’allocation sur le tas, cela sans mot clé explicite (comme malloc en C ou new en C++), il suffit juste de déclarer sa variable avec un & devant, signifiant que l’on manipule une référence.

struct Point {
    x int
    y int
}

// Allocation sur le tas
p := &Point{10, 10}
// Comme en C++, les références ont la même syntaxe pour l’accès aux attributs
println(p.x)

Les mécanismes de gestion automatique de la mémoire dans V sont encore balbutiants. Jusqu’à il y a peu, il fallait libérer manuellement toute ressource allouée sur le tas. L’instruction free étant considérée depuis le début comme non sécurisée (à utiliser seulement via le mot clé unsafe), on comprend que le créateur de V a toujours eu l’intention de rendre la gestion de la mémoire transparente pour l’utilisateur.

Depuis quelque temps, un mécanisme de libération automatique de la mémoire commence à être mis en place .

Encore sous la forme d’une option à l’heure actuelle (-autofree), il permet de ne plus avoir à se soucier de la mémoire et devrait être le mode par défaut dès la prochaine version.

Le compilateur utilise une stratégie hybride (appelée autofree) qui mélange le rajout d’instructions de libération de la mémoire (free) automatiquement lors des cas « faciles » à repérer et du comptage de références dans des cas plus complexes.

Il est encore trop tôt pour dire avec précision quel est le surcoût de ce comptage de références, dans quel cas il est utilisé et si cela constituera la seule stratégie utilisée.

Ce qu’on peut dire, c’est qu’après plusieurs années, cette technique de gestion automatique de la mémoire n’est pas encore au point et que V intègre par défaut un ramasse-miette écrit en C. Ce choix rajoute une dépendance non négligeable au projet, mais permet aux concepteurs du langage de se concentrer sur d’autres problématiques.

Un formateur de code strict

Tout comme Go ou Rust, V inclut un outil de formatage de code très strict qui rend tout code écrit en V semblable.

C’est vraiment une fonctionnalité que j’apprécie énormément dans un langage : on peut se concentrer sur le fond de l’écriture du code. Tout ce qui touche à la mise en forme est gérée automatiquement et on sait qu’on ne peut pas se tromper.

Puis, il faut bien avouer que ça évite les débats au sein d’une équipe :).

Un autre aspect important des formateurs de code est qu’ils permettent de faire évoluer automatiquement une base de code vers les nouvelles syntaxes du langage. Par exemple, le changement du mot clé go vers spawn a été traité automatiquement par v fmt.

On peut noter quelques particularités :

  • seul le style de nommage « snake_case » minuscule est accepté pour les variables et les constantes,
  • pour l’instant toute ligne vide est supprimée (cela va changer).

Une bibliothèque standard ambitieuse

Le créateur du langage, Alex Medvednikov, est très ambitieux au sujet des bibliothèques qu’il souhaite proposer par défaut. L’envie est que V puisse être utilisé dans tous les domaines : de la programmation système à l’écriture d’application graphiques en passant par les services web.

Pour cela, V intègre dans sa bibliothèque standard :

  • une bibliothèque 2D bas niveau (basée sur Sokol)
  • une bibliothèque ui pour faire des interfaces graphiques. Très limitée (et buguée) pour l’instant, mais les ambitions sont d’être pleinement multiplate-forme par défaut, avec notamment une prise en charge d'iOS et Android prévue,
  • un serveur web intégré,
  • une bibliothèque d’accès aux bases de données SQL intégrant la possibilité de coder ses requêtes dans une sorte de SQL, cela grâce aux sucres syntaxiques mentionnés plus haut.

Pour l’instant, il faut l’avouer, tout est dans un état peu avancé. À croire que certaines des bibliothèques ont été commencées sans forcément être développées assidûment. Nous reviendrons en détail sur les critiques que l’on peut porter à V (car il y en a bien sûr).

Ce que j’apprécie c’est l’intention derrière : fournir une bibliothèque standard répondant à la plupart des besoins tout en étant légère et moderne.
Un peu à l’instar de python, mais je l’espère avec des outils d’interface graphique plus modernes que Tk.

Questions en vrac sur le langage

Qu’apporte V par rapport à Go ?

Ce sont des langages très similaires qui rentrent dans la même catégorie, à savoir des langages modernes, compilés et très facile à prendre en main.

Maintenant, si vous êtes frustré par certains manques dans la syntaxe de Go (notamment la gestion d’erreur) ou que vous souhaitez avoir des binaires plus petits et des performances au plus proche du C, V peut être intéressant.

Bien entendu, étant en période d’intense développement, il est encore proscrit pour un usage professionnel.

De plus, son écosystème est bien moins développé que Go.

On pourrait alors se demander pourquoi avoir fait un énième langage ?

Je ne suis pas dans la tête d’Alex, mais je pense qu’il aimait vraiment Go et voulait créer un langage encore plus minimaliste tout en apportant des idées intéressantes provenant d’autres langages. Puis, peut-être, tout simplement pour en apprendre davantage sur les langages de programmation.

Et personnellement, j’adore le résultat !

Est-ce que V est aussi sécurisé que Rust ?

V est décrit comme étant « safe », mais je ne crois pas que le compilateur aille aussi loin que celui de Rust.

Gestion de la mémoire

Un des point fort de Rust est la notion de « possession » (ownership) d’une donnée allouée sur le tas. En effet, seule une variable à la fois peut posséder une donnée allouée, et quand celle-ci sort de sa portée (scope), elle libère automatiquement la mémoire allouée.

Ce mécanisme très abouti permet de garantir que les ressources allouées seront toujours libérées et qu’il n’y aura de problème d’allocation multiple, ou d’accès hors de la mémoire, même en cas de programmation parallèle.

V me paraît sur ce point, moins précis que Rust et la stratégie d’allocation globale du langage n’est pas clairement définie.
Par exemple, dans la documentation, on peut lire que des tampons de mémoire pré-allouée sont utilisés.
C’est sans doute une optimisation bienvenue, mais qui ne conviendrait peut-être pas à tout le monde.

Autres aspects sécurisants
  • Effectue des vérifications lors de l’accès au tableau via index e

par Andréas Livet, BAud, palm123, Ysabeau, Stéphane Bortzmeyer, Aldebaran, Jona

DLFP - Dépêches

LinuxFr.org

Proxmox Virtual Environment 8.2 avec assistant d'import VMware disponible

 -  26 avril - 

Proxmox Server Solutions GmbH a publié la version 8.2 de sa plate-forme de virtualisation libre Proxmox Virtual Environment (VE).La solution (...)


Codeberg, la forge en devenir pour les projets libres ?

 -  25 avril - 

Face aux risques que fait peser GitHub sur le monde des logiciels libres suite à son rachat par Microsoft en 2018, une alternative semble avoir (...)


L’informatique sans écran

 -  21 avril - 

Lors d’un Noël de ma tendre jeunesse pré-adolescente est arrivé un « ordinateur » dans le foyer. Ce PC (Intel 386) a été installé dans le bureau et a (...)


Entretien avec GValiente à propos de Butano

 -  16 avril - 

GValiente développe un SDK pour créer des jeux pour la console Game Boy Advance : Butano.Cet entretien revient sur son parcours et les raisons (...)


Nouveautés d'avril 2024 de la communauté Scenari

 -  11 avril - 

Scenari est un ensemble de logiciels open source dédiés à la production collaborative, publication et diffusion de documents multi-support. Vous (...)