À la découverte du langage V

Posté par  . Édité par BAud, palm123, Ysabeau 🧶 🧦, Stéphane Bortzmeyer, Aldebaran et Jona. Modéré par Julien Jorge. Licence CC By‑SA.
Étiquettes :
54
5
sept.
2023
Programmation

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 et quitte le programme via un panic et un message d’erreur approprié, ce qui évitera de pouvoir accéder à des adresses mémoires hors de la mémoire du programme.
  • Une abstraction du code multi thread via les « goroutine » et les channels rendant l’implémentation de ce genre de code à la fois simple et sécurisée.
  • Les variables immuables par défaut et l’absence d’état global évite aussi certains écueils.
  • Afin d’éviter les erreurs dues à la parallélisation (« race condition »), un mécanisme de mutex, intégré à la syntaxe du langage (rlock) mais c’est encore expérimental.

Il me semble que le compilateur de Rust propose une analyse bien plus poussée et une sécurité accrue.

N’y a-t-il pas trop de « Buzz » autour du langage ?

Il y a beaucoup d’attention autour du langage.
Avec plus de 34 000 étoiles sur GitHub on peut dire que c’est un projet en vogue !
C’est sans doute beaucoup compte tenu de son niveau de maturité !

Aussi, certains critiquent le fait que V n’est pas à la hauteur de ce qu’il annonce.

Je trouve que c’est en partie vrai.

Un article intéressant liste tous les aspects « non conformes » à ce qui est affiché. L’auteur fait parfois preuve d’un peu de mauvaise foi, mais certaines critiques sont justifiées et constructives. Dommage que l’article ait été aussi mal reçu en interne

En vrac :

  • les fonctions sont annoncées comme « pures » par défaut, mais seulement pour dire que les paramètres sont immuables, ce n’est pas la définition de « pure » en programmation fonctionnelle,
  • plusieurs éléments sont mis en avant comme la rapidité de compilation (qui est forcément relative à la complexité du code) et le caractère « safe » du langage,
  • il est mentionné que la recharge du code « à chaud » (Hot code reloading) est possible, mais cela reste limité aux fonctions ayant un attribut spécifique. Ce n’est donc pas de la recharge à chaud « générique », comme on peut la trouver dans des langages comme Dart,
  • V ui, la bibliothèque graphique mise en avant est tout de même très minimaliste et — pour l’instant — peu avancée,
  • dans certains aspects, la documentation ne reflète pas l’état actuel du langage, mais ce à quoi il doit ressembler. Ça manque un peu de rigueur…

J’ai l’impression que le créateur du langage a lancé plein de pistes et a posé une intention et quelques bouts de code par-ci, par-là, mais rien de forcément fini.

Quand on voit qu’il code en même temps : un langage, un éditeur de texte, un client natif pour les plateformes de discussion instantanées, une bibliothèque graphique, un framework web, un ORM, un gestionnaire projet… et souhaite même s’attaquer à un navigateur web ! N’importe qui de sérieux se dit que chacun de ces projets nécessite à minima une personne à plein temps dessus.

Pour contrer ce portrait un peu négatif, ce qui est chouette, c’est de voir l’enthousiasme généré autour du langage et que rapidement beaucoup de personnes se sont mises à contribuer.
Il n’y a qu’à voir la liste des commits pour se rendre compte que V n’est plus développé par une seule personne et ça, c’est rassurant pour l’avenir du langage.

Disons qu’Alex a pris à cœur l’adage du libre « release soon, release often », le tout dans une bonne ambiance de « bazar" ». La rigueur pourra venir après :).

Personnellement, j’ai découvert plusieurs bugs dans le langage et ai pu contribuer facilement à leur correction. Soit en proposant du code quand cela me paraissait facile (j’ai, par exemple, réécrit la fonction split des string) ou en fournissant du code de test pour aider à la correction de bogue. Dans les deux cas, je me suis senti très bien accueilli et les contributions furent faciles.

Cette discussion reddit à propos des critiques sur V est intéressante.

Conclusion

V est un petit langage très sympathique. Extrêmement proche de Go, il s’en distingue néanmoins par des emprunts très bien choisis à d’autres langages (comme Rust) et d’autres paradigmes (notamment la programmation fonctionnelle).
Le fait qu’il ne nécessite pas de « runtime », ni de ramasse-miette (bon, c’est moins vrai aujourd’hui), le rendent sans doute plus intéressant quand les performances ou la taille des binaires est un enjeu.

Le tout fait un langage très moderne tout en étant incroyablement facile à apprendre et utiliser au quotidien.

Même s’il est encore jeune et avec un écosystème peu mature, il est rapidement devenu un langage que j’affectionne particulièrement. Il n’est bien entendu pas encore mature pour du code utilisé en production, mais vaut vraiment le détour.

De plus, V est un langage accessible (hackable) pour celui qui veut apprendre comment fonctionne un compilateur et expérimenter dans ce domaine.

Aller plus loin

  • # variable immuable ?

    Posté par  . Évalué à 8.

    une variable ça varie tandis qu'immuable c'est pas censé bouger. Une variable immuable, c'est un peu paradoxal ;-)

    je pense que je ne comprend pas bien le concept d'immuable dans ce contexte. par exemple dans la boucle for, on peut avoir i++, donc i varie, ça ne me semble pas immuable.

    • [^] # Re: variable immuable ?

      Posté par  (site web personnel, Mastodon) . Évalué à 8.

      En général, en programmation, le terme de variable immuable (qui peut effectivement faire rire) est destiné aux cas où la valeur n'est pas connue statiquement mais, une fois affectée, ne peut plus changer. Ce n'est effectivement pas le cas en Go (je n'ai pas essayé V).

    • [^] # Re: variable immuable ?

      Posté par  . Évalué à 1.

      Oui une variable immuable c'est pas très logique, mais c'est pour les différenciées des constantes comme ça a été dit.

      Pour le cas du for, c'est un cas particulier où le compilateur ne nécessite pas le mot clé mut (voir le guide).

      • [^] # Re: variable immuable ?

        Posté par  . Évalué à 8.

        C’est le même nom qu’en math, pour les paramètres des fonctions par exemple. Pourtant les variables en maths sont immuables, si tu as le même nom de variable dans le même scope, en deux endroits différents leur valeur sera toujours égale.

    • [^] # Re: variable immuable ?

      Posté par  (site web personnel) . Évalué à 6.

      Par exemple en python les int sont immuables.
      Si on fait :

      a = 1
      print(id(a))
      a += 1
      print(id(a))
      b = 1
      print(id(b))

      On s'aperçoit que la première variable a changé d'identité avec la réaffectation, alors que b…
      Il faut bien avouer que les raisons sous-jacentes ne me semblent pas très évidentes. Peut-être un rapport avec le fait qu'il serait bizarre que tous les contenus d'un conteneur immuable puissent être changés.

      « IRAFURORBREVISESTANIMUMREGEQUINISIPARETIMPERAT » — Odes — Horace

    • [^] # Re: variable immuable ?

      Posté par  . Évalué à 4.

      Interessant l'utilisation de variable immutable sans "tricher" et autoriser le masquage de nom comme le fait Rust.
      D'un coté c'est + clair, un nom == une valeur d'un autre je soupçonne que cela induit a creer beaucoup de nom et qu'on finit par faire toto := …; toto2 := … ;

    • [^] # Re: variable immuable ?

      Posté par  . Évalué à 4.

      Ce qui me choque plus, moi, c'est qu'à l'hôpital, on trouve intelligent de surveiller les constantes des patients…

      • [^] # Re: variable immuable ?

        Posté par  . Évalué à 7.

        Ben la température c'est une constante physiologique, parce que le corps essaye de la garder toujours a la même valeur malgré les variations de l'environnement. Si elle s’écarte de sa valeur habituelle, c'est le signe qu'il y a un problème. De meme, si dans ton programme t'a déclaré une constante mais que tu t’aperçois qu'elle change de valeur, c'est signe qu'il y a un problème

  • # Merci pour cette description détaillée !

    Posté par  (site web personnel) . Évalué à 5.

    Merci pour cette dépêche et cette découverte du langage, c’est vrai que les exemples sont parlants et donnent une bonne idée de ce que l’on peut faire avec !

    Si je comprends, le langage ne contient pas de Null et passe par un type ['a] Optionnal, est-ce que cela peut être représenté au niveau du pattern matching ? Leurs exemples n’indiquent pas comment décomposer le type en question.

    Le langage m’a l’air intéressant, (j’aime le ratio entre simplicité du code et garantie des types) et leur librairie standard semble bien fournie.

    Je vais lui donner sa chance :)

    • [^] # Re: Merci pour cette description détaillée !

      Posté par  . Évalué à 3.

      Si je comprends, le langage ne contient pas de Null

      Oui c'est l'idée, après je ne suis pas allé dans le détail, mais comme V permet de travailler avec des bibliothèques C sans couche d'interopérabilité, la notion de nil est présente dans le langage, mais a utilisé avec précaution.

      Il est aussi possible de déclarer des champs de structure avec comme valeur par défaut 0 soit un pointeur null

      J'ai un peu du mal à comprendre pourquoi un des cas doit être marqué unsafe et l'autre non.

      Personnellement, je n'aime vraiment pas cette notion de unsafe dans V, tout comme je ne l'apprécie pas dans Rust. Je préfère de loin l'approche de Zig sur ce point.

      Comme je le dis dans la dépêche, V cherche à faire un grand écart entre facilité d'utilisation, typage fort, rapidité et lien direct avec le C. Forcément, certains compromis doivent être fait, la notion de unsafe en est un.

      et passe par un type ['a] Optionnal

      Oui, c'est peut-être mieux détaillé dans cette partie du guide (voir l'exemple à la fin).

      Donc pas de pattern matching pour gérer les Optional mais soit via or, soit via un if/else comme décrit dans cette doc.

      Exemple :

      fn get_ten_or_none(val int) ?int {
          if val == 10 {
              return 10
          }
          return none
      }
      
      fn main() {
          if val := get_ten_or_none(9) {
              println('val : ${val}')
          } else {
              println('no value')
          }
      
          val2 := get_ten_or_none(10) or { 5 }
          println('val2 : ${val2}')
      }
  • # curl|sh

    Posté par  (site web personnel) . Évalué à 10.

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

    Chaque fois qu'un projet fait ça, Dieu étrangle un chaton avec un fil de souris.

    Le post ci-dessus est une grosse connerie, ne le lisez pas sérieusement.

    • [^] # Re: curl|sh

      Posté par  . Évalué à 4.

      J'ai vérifié et en fait je me suis trompé ou c'est que ça a changé depuis que j'ai commencé la rédaction de la dépêche (il y a plus de 2 ans !).

      En fait, une version transpilée du compilateur V est compilée avec gcc par défaut (voir le Makefile).

      Par contre, tcc est bien utilisé pour compiler les binaire une fois que le compilateur V à transformé le code V en C.

      Et il semble bien téléchargé par make (si on en croit la doc), je ne trouve juste pas où se fait ce téléchargement.

      En quoi est-ce si mal ?

      • [^] # Re: curl|sh

        Posté par  (site web personnel) . Évalué à 10.

        Que si la machine ou tu veux compiler V n'a pas d'accès à Internet, tu ne pourras pas le faire, en plus d'implications au niveau sécurité si tu récupères un TCC vérolé…

        Et certainement un tas d'autres raisons !

        • [^] # Re: curl|sh

          Posté par  . Évalué à 1.

          Le fait d'installer V nécessite une connexion internet et il a sa propre version de tcc.

          Mais même s'il pointait sur une release de TCC officielle par exemple, je ne vois pas en quoi ça serait plus dangereux.

          C'est une question de confiance, si tu fais confiance à ce projet pour télécharger du code sur son dépôt, alors tu as aussi confiance au code dont il dépend.

          A noté que V ne nécessite pas de droits super utilisateur pour s'installer.

          C'est à la personne qui installe de prendre la responsabilité de réaliser le lien symbolique.

          • [^] # Re: curl|sh

            Posté par  (site web personnel) . Évalué à 9.

            Ben vouloir pouvoir compiler un truc petit programme sans accès internet, c'est pas forcément un besoin atypique (rien que quand on est en déplacement).

            Par ailleurs, ce mode de fonctionnement n'implique pas juste de faire confiance à l'auteur au binaire hébergé au moment où tu installes le soft, mais de considérer que ce binaire ne sera jamais corrompu, volontairement ou pas, et qu'il sera toujours disponible.

            Par exemple, s'il était corrompu (genre fichier incorrect), tu ne peux plus bosser tant que l'auteur ne corrige pas le problème

            Autre exemple, si l'hébergement s'arrête (facture non-payée, compte fermé par l'hébergeur pour n'importe quelle raison), tu ne peux plus bosser non plus.

            De même, si le fichier hébergé se fait remplacer par un programme malicieux, ça veut dire que tu exécuteras malgré toi un binaire arbitraire sur ta machine sans forcément t'en rendre compte. Est-ce qu'il y a un check d'intégrité de ce programme à chaque exécution ?

            Voilà des exemples de problèmes que ça pose, si j'ai bien compris le contexte :)

            • [^] # Re: curl|sh

              Posté par  . Évalué à 2.

              J'arrive un peu tard, mais je me demande si vous parlez de la même chose.

              L'auteur de la dépêche dit que TCC est téléchargé pendant l'exécution du make. Mais c'est bien le make qu'on lance une fois pour compiler le transpileur V. C'est pas à chaque fois qu'on compile un programme écrit en V.

              • [^] # Re: curl|sh

                Posté par  (site web personnel, Mastodon) . Évalué à 2.

                Si, si, il s’agit bien du téléchargement fait par le Make sans qu’on puisse garantir quoi que ce soit.

                “It is seldom that liberty of any kind is lost all at once.” ― David Hume

    • [^] # Re: curl|sh

      Posté par  . Évalué à 6.

      Il y a encore plus rapide, quand tu fais "make" ça télécharge le binaire du logiciel! Compilation super-rapide si tu as la fibre.

  • # Go <-> V

    Posté par  . Évalué à 4.

    D'après cette excellente description il serait possible de générer du code Go à partir du V et inversement et plus facilement que du C, est-ce que ça a été tenté ?

    • [^] # Re: Go <-> V

      Posté par  . Évalué à 4.

      Oui c'est possible et ça a été tenté, il semble exister un backend Go (en tout cas il y a un canal discord sur le discord officiel), mais je ne crois pas que ce soit allé bien loin.

      De manière générale, je trouve que V est pour l'instant tellement lié au C (certains attributs sont directement liés à C) que je ne pense pas qu'un autre backend viable verra le jour d'ici peu.

      Ou alors, il faudra revoir certains éléments du langage.

  • # Variable magiques ?

    Posté par  . Évalué à 5.

    C'est l'it qui sort du bois dans le map, qui rencontre un a qui se croit supérieur au b lorsqu'il sort, mais en cas de problème, il err sans exception.

    Magnifique… Je suppose qu'écrire 'a,' ou 'it,' pour pouvoir choisir le nom de la variable dans une fonction lambda eu été trop de dépense d'énergie, résultat, il y a des variables magiques qui sortent de nulle part, qui empêche (du moins, je l'espère) l'utilisation de ces noms partout ailleurs (c'est clair que it, a, err sont jamais utilisées dans du code standard).

    Bref, c'est nul comme choix.

    • [^] # Re: Variable magiques ?

      Posté par  . Évalué à 3. Dernière modification le 05 septembre 2023 à 12:29.

      C'est sûr, c'est un choix contestable. Perso, ça m'a choqué au début, mais finalement j'aime bien, c'est très compact et comme c'est des variables qu'on nomme souvent de la même manière, ça force une consistance dans le code.

      qui empêche (du moins, je l'espère) l'utilisation de ces noms partout ailleurs

      Non on peut les utiliser ailleurs.

      fn get_ten_or_none(val int) !int {
          if val == 10 {
              return 10
          }
          return error('${val} not found')
      }
      
      fn main() {
          err := "yooo"
          if val := get_ten_or_none(10) {
              println('val : ${val} ${err}')
          } else {
              println('${err}')
          }
      }

      Ce qui me fait dire que la redéfinition de variable est finalement possible dans ce cas là…

      • [^] # Re: Variable magiques ?

        Posté par  . Évalué à 3.

        Ah, c'est encore pire que ce que je pensais.

        Dans cet exemple de code, comment accèdes-tu à la variable err "globale" dans le scope du else ?

        Si tu ne peux pas, dans ce cas, impossible d'utiliser une variable err ou a ou b nulle part, car le jour où la fonction gen_ten_or_none change de signature (suite à une maintenance de code par exemple) pour retourner une erreur, une variable err vient "shadow aliaser" tes propres variables sans aucun message d'alerte.

        Autant dans ce cas, définir err et it et a et b comme mot clé du langage. C'est assez déroutant en fait.

        De plus map prend un "fonction scope" comme argument et pas un "parameter list" du coup, ce qui fait un peu mélanges d'effluves, car les autres fonctions attendent toutes un "parameter list".

        Comment faire pour avoir un code plus long dans un map ? (Genre 2 lignes, ou assigner une variable externe lors de l'itération?).

        • [^] # Re: Variable magiques ?

          Posté par  . Évalué à 1.

          Comment faire pour avoir un code plus long dans un map ? (Genre 2 lignes, ou assigner une variable externe lors de l'itération?).

          Il est possible de passer aussi une fonction callback à map.

          Après sinon je suis d'accord, ces "compile magic" sont déroutant et ça donne un côté très bidouille au langage, mais dans l'histoire des langages, c'est pas forcément les langages les mieux défini (ou les plus pures) qui ont eu les faveurs des programmeurs.

          Dans le cas de V, les concurrents sont bien en place, je doute qu'il arrive un jour à devenir mainstream.

          Quoiqu'il en soit, c'est une belle expérimentation, j'aime notamment l'idée de vsh. Poussé un peu plus loin, ça peut donner quelque chose de très séduisant.

          Perso, je verrai bien une sorte de zx mais codé en V. J'avais commencé un truc, mais sans aller bien loin…

    • [^] # Re: Variable magiques ?

      Posté par  . Évalué à 4. Dernière modification le 06 septembre 2023 à 08:09.

      Pour err je demande à voir, mais pour it ça existe en kotlin, en groovy, en perl (bon $_ va bien plus loin) et probablement dans d'autres. C'est plutôt agréable à utiliser en pratique. Ça peut se rapprocher des this et self de la majorité des langages objet.

      https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

      • [^] # Re: Variable magiques ?

        Posté par  . Évalué à 3.

        Err ça existe aussi en VB

      • [^] # Re: Variable magiques ?

        Posté par  . Évalué à 6.

        Pour err je demande à voir, mais pour it ça existe en kotlin, en groovy, en perl (bon $_ va bien plus loin)

        Je ne suis pas sûr qu'on puisse comparer V et Perl pour se faire un avis sur la pertinence ou pas des variables magiques.

        Le contexte n'est pas vraiment le même.

        Perl a été à l'origine conçu surtout pour être un remplaçant sous stéroïdes de sed+awk. Bref, c'est parfait pour faire des oneliners dans le terminal, mais pas forcément pour faire des longs scripts.

        En ce sens, les variables magiques telles que $_ (mais aussi $", $!, etc.), ont du sens. Le but étant de taper le moins de caractères possibles histoire de gagner de la place (les écrans n'étaient pas larges) et du temps (en cli, on est plus souvent en RO qu'en RW).

        Par ailleurs, les noms de variables sont quand même assez exotiques. Il y a plus de chances de spontanément nommer une variable a, b, err ou it que $_ ou $!. Ceci dit, on pourrait parler des noms longs. Par exemple $_ peut aussi s'écrire $ARG, $! peut s'écrire $ERRNO ou $OS_ERROR. Ce qui est d'ailleurs recommandé quand on écrit un script.

        Ça peut également être source de confusion, c'est vrai. Mais globalement si on n'utilise pas de variables en majuscules ça passe.

        Tout ça pour dire que même si en Perl également, le principe des variables magiques peut être critiqué (et il l'est depuis des dizaines d'années), je trouve quand même ça moins gênant que pour V.

        Mais c'est un jugement tout à fait subjectif.

        • [^] # Re: Variable magiques ?

          Posté par  . Évalué à 3.

          Je pourrais entendre ce que tu dis si 15 ans plus tard en ayant du recul sur le langage et en s'autorisant à tout casser ils n'auraient pas accouchés de Raku qui a lui aussi des variables magiques de ce genre : The $ variable.

          le principe des variables magiques peut être critiqué

          Mon argument n'est pas de dire est-ce que c'est bien ou mal, mais que ce n'est pas objectivement et fondamentalement mauvais. Il n'est pas du tout le seul langage à le faire et ces langages vivent très bien. C'est un choix de fonctionnalité du langage comme tant d'autres.

          https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

  • # Taptempo

    Posté par  . Évalué à 10.

    La seule question qui importe est « existe-t-il une version de TapTempo écrite en V » ?

    Il semble que non.

    • [^] # Re: Taptempo

      Posté par  (site web personnel, Mastodon) . Évalué à 7.

      Yapluka !

      « Tak ne veut pas quʼon pense à lui, il veut quʼon pense », Terry Pratchett, Déraillé.

      • [^] # Re: Taptempo

        Posté par  . Évalué à 4. Dernière modification le 07 septembre 2023 à 21:22.

        Et bah voilà !

        J'ai codé ça rapidement, j'utilise une petite bidouille avec le raw mode du module readline, mais ça fonctionne.

        J'essaierai de voir si je peux pas faire plus simple au niveau de la récupération des inputs et du mode raw.

        Pas encore pris le temps d'en faire un journal, mais ça va venir.

  • # À propos de la programmation orientée objet

    Posté par  (site web personnel, Mastodon) . Évalué à 10.

    Ce qui est décrit ici n’est principalement pas de la programmation orientée objet :

    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.

    Ceci n’est pas une attaque contre l’auteur, car comme il le dit lui-même, c’est ainsi que la POO est présentée dans beaucoup trop de cas.

    Une définition plus exacte de la POO serait :

    La programmation orientée objet – ou POO – est un paradigme de programmation informatique qui organise le code en un assemblage de briques appelées objets (merci Captain Obvious).

    Ces objets ont un état interne et un comportement, qui leur permet d’interagir entre eux.

    Et… c’est tout.

    Je vous renvoie à ce billet pour plus de détails quant à ce que ça implique, et si comme moi il y a quelques années ou comme l’auteur de la présente dépêche vous avez été déçus par ce qu’on vous a appris, vous pourriez être surpris.

    La connaissance libre : https://zestedesavoir.com

    • [^] # Re: À propos de la programmation orientée objet

      Posté par  . Évalué à 4.

      c’est ainsi que la POO est présentée dans beaucoup trop de cas.

      Oui, mais c'est aussi ainsi qu'elle est mise en pratique dans certains langages comme tu le dis bien dans ton billet.

      Franchement, j'ai du défaire tout l'enseignement que j'ai eu à ce sujet, ça été douloureux, car j'y ai cru à toutes ces choses !

      Je crois que le pire c'est les design pattern, j'ai essayé de m'y mettre ! J'y ai mis du cœur, mais dans bien des cas, je trouve que ça complexifie le code…

    • [^] # Re: À propos de la programmation orientée objet

      Posté par  (site web personnel) . Évalué à -6.

      Je vous renvoie à ce billet pour plus de détails quant à ce que ça implique,

      Cool !

      Si le principe d’encapsulation est correctement respecté (ce qui devrait toujours être le cas en théorie mais ne l’est pas en pratique), l’état interne d’un objet est inaccessible de l’extérieur, et ne peut pas être manipulé directement.

      Ah beh non, pas cool : le mec d'entrée confond encapsulation et masquage…. => /dev/null

      • [^] # Re: À propos de la programmation orientée objet

        Posté par  (site web personnel, Mastodon) . Évalué à 10.

        « le mec » c’est moi. Merci pour ce retour pertinent et constructif (non).

        Pour répondre quand même à ta remarque : le fait qu’il y ait, ou non, une différence forte entre « encapsulation » et « masquage » n’est pas un consensus du tout. Par exemple, si Wikipedia FR semble faire une différence, Wikipedia EN mentionne le fait qu’en pratique les termes sont souvent interchangeables et que tout le monde n’est pas d’accord pour faire une distinction et ce dans les deux articles. Dans les faits, quand on parle d’encapsulation dans le cadre de la POO, c’est pratiquement toujours avec masquage, d’où ce choix dans mon article – notamment parce qu’une information non masquée a toujours un risque d’être utilisée (au moins lue) hors de son contexte d’encapsulation.

        Maintenant si tu veux pinailler sur des détails, je te conseille de le faire de façon constructive et ça a une chance d’être effectivement pris en compte (le message que je pointe l’a été) plutôt que de prétendre que tout est bon pour la poubelle parce qu’un détail ne t’a pas plu. Ce conseil est valable à plus grande échelle que celle de ce billet.

        La connaissance libre : https://zestedesavoir.com

        • [^] # Re: À propos de la programmation orientée objet

          Posté par  (site web personnel) . Évalué à -5.

          Merci pour ce retour pertinent et constructif (non).

          Ok, je vais détailler ça sera peut-être utile et moins frustrant.
          Mais bon, Tu fais un billet d'humeur (non constructif) sur la POO, son usage et son enseignement pourris selon toi, je fais un post d'humeur sur ton billet d'humeur pourri selon moi => où est le problème ?

          Les deux phrases m'ayant déclenché cette réaction :

          voici un billet sur la programmation orientée objet et pourquoi elle est mal utilisée et enseignée.

          et

          Si le principe d’encapsulation est correctement respecté (ce qui devrait toujours être le cas en théorie mais ne l’est pas en pratique), l’état interne d’un objet est inaccessible de l’extérieur, et ne peut pas être manipulé directement.

          plus le rapide survol des titres :

          Un enseignement complètement à la rue (…) l’immense majorité des cours de POO n’expliquent pas ce qu’est la POO : ils expliquent une vision défectueuse de la POO (…) Des langages qui font n’importe quoi.

          Donc ça balance sévère et paf, d'entrée une confusion entre deux concepts, l'un fondamental en POO (l'encapsulation), l'autre accessoire (le masquage), je trouve que ça part assez bien pour qu'il ne soit pas nécessaire de continuer et faire savoir que ça vaut pas plus qu'une discussion de comptoir avec Raymond au PMU.

          Ça c'est pour expliquer la forme succincte de ma critique dont le fond a semble t'il parfaitement été compris : j'ai pas été mauvais sur le coup en termes de com.

          Sur le fond :

          Dans les faits, quand on parle d’encapsulation dans le cadre de la POO, c’est pratiquement toujours avec masquage,

          Le masquage est un autre concept comme la notion de classe ou de propriété.
          Et quand on parle de POO on parle pratiquement toujours dans un contexte de langage à classe (C++, Java, C#…. ) : ça rend la classe implicite ou obligatoire quand on parle POO ? Fuck les prototypes ?

          Donc ok, c'est proche et ça va souvent ensemble mais ce n'est ni implicite, ni obligatoire il me semble.

          parce qu’une information non masquée a toujours un risque d’être utilisée (au moins lue) hors de son contexte d’encapsulation.

          Et alors ? En pratique c'est presque impossible de l'empêcher donc bon…

          Le masquage c'est comme la notion de classe ou d'encapsulation (bundling, que j'utiliserai comme terme ensuite, pour éviter la confusion) : un concept.

          Après tu l'implémentes (outils) comme tu veux :
          - mot clef et mécanisme du langage (ex : private)
          - convention de nommage et savoir vivre des codeurs
          - menace de mort de l'ordinateur (comme certains registres du TI99 ;) )
          Tout pareil pour le bundling :
          - mot clefs et mécanismes du langage (ex : class)
          - convention de nommage / structures particulières
          - il y a surement d'autres méthodes que je ne connais pas

          Donc soit tu parles du concept (on peut cacher des choses) soit tu parles des outils (on le rend private), soit tu parles des pratiques (c'est bien/c'est mal).

          Là tu nous parles de POO et d'encapsulation au sens concept car c'est dans une définition.
          Je ne vois pas du tout ce que la morale ou les outils viendraient faire la dedans.

          Surtout, la POO n'interdit pas d'aller utiliser les propriétés et autres comportements : elle se base justement la dessus (via les messages).

          Ta définition en deux phrases est d'ailleurs incomplète : il manque les messages, mais pour ça il fallait mieux lire entre les lignes WP que tu as résumées, car il y a une proposition clef : "ils savent interagir entre eux".
          Et il fallait la comprendre : ce n'est pas leur comportement qui fait qu'ils savent interagir entre eux, c'est le mécanisme de messages qu'ils utilisent, généralement via l'appel de méthode (une implémentation possible).
          Donc non ce n'est pas tout, merci Captain pas si Obvious que ça.

          C'est peut-être aussi un détail mais c'est souvent là où se cache le diable : le fait que les objets utilisent un mécanisme de communication pour interagir entre eux me semble légèrement plus nécessaire que le masquage dans une approche POO…

          une différence forte entre « encapsulation » et « masquage » n’est pas un consensus du tout

          Il se peut, ma mémoire étant ce qu'elle est et ma littérature de référence sur le sujet étant perdue dans les limbes, tu as peut-être raison. En tous cas, même WP FR semble te donner raison.

          Et je me demande bien alors comment on appelle le bundling en français…. Pas des structures : il n'y a pas de comportement dedans.
          Des paquetages ?

          J'instancie des paquetages en python et en perl ? Vraiment ?

          Pour moi intégrer le masquage dans l'encapsulation n'est ni plus ni moins qu'un abus de langage par anglicisme (eux ont les 3 mots : encapsulation, bundling, information hiding). Dommage qu'il se répande dans la littérature.

          n’est pas un consensus du tout

          Quitte à jouer sur les mots et les consensus, est-ce qu'on est toujours dans de la POO quand l'instance d'une classe n'a ni toutes les méthodes ni tous les attributs de sa classe ? On encapsule, on a bien un objet issu d'une 'classe' et pourtant y a comme un truc qui pue….
          Je relève les copies dans 3 mois, bon courage pour aller chercher un consensus dans la littérature.

          tout est bon pour la poubelle parce qu’un détail ne t’a pas plu

          Un concept central n'est pas un détail dans un papier dont c'est le sujet. J'aurais lu ça dans Marie Claire, je n'aurais probablement relevé.

          Vois le bon côté des choses, j'ai lu un peu le reste pour te répondre et d'ailleurs j'ai deux questions :
          - le coup des List qui intègrent des méthodes de modification => implémenter List pour faire un truc immuable ça me semble complètement con (sauf contexte étrange, genre hack moisi pour se sortir d'un mauvais pas en loucedé), tu as un exemple de classe dans la StdLib java qui fait ça ? Avec 15 ans d'XP tu as surement ça sous le coude.
          - tu parles de Kotlin comme 'Langage a JVM' : j'ai cru comprendre que Kotlin se compilait / transpilait pour différentes archi (JVM, natif, JS notamment). Tes mesures ne concernent donc que quand on le fait tourner l'exe sous JVM, mais quid pour les autres supports ? Ou j'ai pas bien capté ce qu’était Kotlin ?

          Pour le reste, de mon point de vue ton billet d'humeur me fait penser que
          - tu n'a pas bien compris certains concepts de base (encapsulation/bundling, masquage, message)
          - tu mélanges concepts (POO), bonnes pratiques (LSP, SOLID, traits) et outils de la POO intégrés au langage (la notion de private, les Enums qui sont des classes de Java)
          - tu aurais pu parler des friends ou d'héritage en diamant : la aussi y a du lourd dans la série bonnes pratiques :D
          - j'ai l'impression que tu galères parce que tu n'as pas bien compris, gênant après 15 ans de Java… Mais bon JBoss-like, Spring, Hibernate et autres machineries du type ne sont pas Java …. Si ça fait 15 ans que tu fais du légo avec ça, ça peut se comprendre et je compatis pour la frustration…
          - tu repompes joyeux les phrases vides de sens de WP (ou sa source), exemple : le laïus sur la conception qui est primordiale en POO => parce que la conception c'est secondaire dans les autres paradigmes ?
          - tu balances des dogmes (LSP, SOLID) qui ont les mêmes problèmes que ceux que tu critiques dans l'enseignement de la POO => ils sont aussi potentiellement des freins à la qualité et à la robustesse (notions bien subjectives) de ton code.
          - après 15 ans de métier, je trouve étonnant que tu parles de code sans parler de contexte, pourtant le contexte, quand il change, c'est ce qui fait que tu vas tout péter, bonnes pratiques ou non, POO ou spaghetti style. C'est surtout ce qui fait qu'un choix est pourri sur papier, mais qu'en fait c'est pas si con IRL. Qu'il est malin aujourd'hui et complètement débile demain.

          Donc, après avoir lu vaguement, ça confirme ma première évaluation : un truc où en quelques lignes je détecte déjà des problèmes sur le cœur du sujet alors que je ne suis pas un cador du domaine => /dev/null. Le reste ne vaudra très probablement pas mieux. C'est pour ça que d'habitude je ne lis pas les billets d'humeurs : les avis de Raymond sur les ZFE et le changement climatique ne valent pas mieux que les miens. J'ai déjà une analyse moisie du monde : la mienne. Pourquoi aller en chercher ailleurs ? Là je me suis fait avoir dans un moment de faiblesse :D
          Mais bon, ça m'a permis de réviser mes classiques en m'amusant, en cela, ça n'a pas été totalement inutile donc merci.

          Allez pour décrisper, j'avais lu un truc qui m'avait fait marrer (sans doute ici) : le code c'est comme le pet, on ne supporte que le sien :D

          • [^] # Re: À propos de la programmation orientée objet

            Posté par  . Évalué à 8.

            Allez pour décrisper, j'avais lu un truc qui m'avait fait marrer (sans doute ici) : le code c'est comme le pet, on ne supporte que le sien :D

            C'est une réflexion de mauvais développeur ça (on en reconnaît certains à leur pédanterie, leur positionnement sur la courbe Dunning-Kruger ne leur permet plus d'apprendre ou leur manque de confiance les pousse à rabaisser, mais ça n'est pas une fatalité).

            https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

          • [^] # Re: À propos de la programmation orientée objet

            Posté par  (site web personnel, Mastodon) . Évalué à 9. Dernière modification le 07 septembre 2023 à 00:48.

            Wow. Ce pavé est incroyable de mépris et de condescendance. J'ose espérer que tu en étais conscient en l'écrivant et que tu n'es pas persuadé avoir écrit un message constructif, parce que ça n'est pas le cas. Une astuce pour faire la différence quand tu rédiges ce genre de réponse à un texte : quand tu contredis le message, c'est une bonne chose (surtout si tu le fais avec des arguments et des sources). Quand tu attaques l'auteur voire que tu l' insultes (ton message souvent très proche de la limite, et parfois du mauvais côté), tu es juste inutile et tu perds ton temps. Pareil quand tu hasardes des hypothèses sur mon parcours ou sur ce que je développe, ou sur les langages que tu ne connais pas (comme Kotlin).

            Oh, à propos de Kotlin, je te conseille de regarder la doc le leur implémentation standard de List : elle est immuable (source : https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/). Le langage s'est créé pour avoir une version moderne et pragmatique de Java (à l'époque où ce dernier stagnait), la notion de "liste immuable par défaut" était assez importante pour la mettre au cœur du langage. Et à l'usage tu n'as pas tant besoin que ça des listes modifiables.

            La connaissance libre : https://zestedesavoir.com

            • [^] # Re: À propos de la programmation orientée objet

              Posté par  . Évalué à 6. Dernière modification le 07 septembre 2023 à 10:29.

              Ne te plains pas il t'a quand même jugé digne d'avoir une réponse de lui ! To pourrais peut être tirer quelque chose de son intervention pleine de sagesse, manifestement ça n'a pas l'air d'être le cas de toit le monde.

              • [^] # Re: À propos de la programmation orientée objet

                Posté par  (site web personnel, Mastodon) . Évalué à 6.

                Le pire c’est que oui, il y a sans doute des trucs intéressants à tirer de tout ça, ne serait-ce qu’une précision en note de bas de page sur la notion d’encapsulation et comment elle s’articule avec celle du masquage.

                Mais j’avoue que j’ai un peu la flemme à retrouver les passages pertinents entre les passages méprisants et/ou injurieux pour ensuite essayer de retrouver des sources (non fournies) qui s’y rapportent pour ensuite voir ce que j’aurais à mettre à jour dans mon article. Et c’est bien dommage.

                La connaissance libre : https://zestedesavoir.com

            • [^] # Re: À propos de la programmation orientée objet

              Posté par  (site web personnel) . Évalué à -4.

              Le dernier pour la route, après j'arrête, c'est en effet totalement inutile ce genre de flamewar. Tu ne comprends pas que je critique ton billet, ta méthode d'argumentation qui porte les biais que tu critiques et non toi en tant qu'humain.

              un exemple sur ta méthode vaut mieux qu'un long discours alors je te cite :

              Pareil quand tu hasardes des hypothèses sur mon parcours

              Donc, dans le paragraphe au titre respectueux de "Des langages qui font n’importe quoi", on a

              Si je prends l’exemple de Java que je connais le mieux (vu que je bosse avec depuis près de 15 ans)

              suivi de

              Des chaines d’héritage qui n’ont aucun sens et qui conduisent à des comportements aberrant (note bas de page n°2).

              suivi de

              Et là je parle bien du langage lui-même, et pas de l’utilisation qui en est faite.

              La note n°2 dit :

              Par exemple, l’interface List expose des méthodes de modification, qui sont donc présentes y compris sur les listes non modifiables, qui ont donc des méthodes qui lancent des exceptions quand on les appelle

              Ce qui m'amène à te poser la question suivante dans mon post précédent :

              implémenter List pour faire un truc immuable ça me semble complètement con (…), tu as un exemple de classe dans la StdLib java qui fait ça ? Avec 15 ans d'XP tu as surement ça sous le coude.

              et tu me réponds dans le post précédent :

              Oh, à propos de Kotlin, je te conseille de regarder la doc le leur implémentation standard de List : elle est immuable

              Bon, soit tu ne comprend pas ma question (peu probable, enfin j'espère), soit tu ne prends pas la peine de te documenter pour me répondre (très gentil, merci), soit tu as dit de la merde dans le billet d'origine, tu t'en es aperçu et ouille ça pique.

              Mais tu en rajoutes une couche surréalistement péremptoire :

              Et à l'usage tu n'as pas tant besoin que ça des listes modifiables.

              Source : "c'est connu Khalessi"

              non franchement, tout ton billet est sur le même moule, tu craches ta frustration, ta confusion entre les choses … => /dev/null

              Et là tu décales le problème sur une attaque ad hominem, crois moi il n'y en a pas vraiment. Je critique principalement le fait d'avoir produit un contenu de merde parce que tu ne cherches même pas avant publication à vérifier si tu dis de la merde ou non, le principe même du billet d'humeur.
              C'est ce qui fait la différence entre une doc et Raymond au PMU : la première bénéficie d'un travail de relecture.

              La seule attaque 'personnelle' que j'assume c'est que tu craches sur les autres 'qui font n'importe quoi et ça c'est cool mais que bien sur la réciprocité est agressive…

              import java.lang.reflect;

  • # Joie

    Posté par  (Mastodon) . Évalué à 8. Dernière modification le 05 septembre 2023 à 14:20.

    Je n'utilises pas et n'ait pas besoin de Volt, et çapusaipalibre mais ceci me réjouit:

    What language is Volt written in?

    V. It's a new language I created to develop Volt. You can read about it here.

    Volt uses native UI APIs: (Cocoa on macOS, Win32 API on Windows, GTK+ on Linux).

    et

    Is it really only 300 KB? Or is it just an installer that downloads another 300 MB?

    No, it's not an installer. The entire app fits into a ≈300 KB ZIP archive, and the only extra thing it downloads is ≈300 KB of high res icos, which are not required for the app to work.

    In the age of 300 MB chat clients and 10 MB web pages we started to forget how powerful our computers are and how much can fit in 1 MB. A lot of time and resoures were spent to ensure the small size and great performance.

    • [^] # Re: Joie

      Posté par  . Évalué à 4.

      Oui c'est cette approche minimaliste qui m'a plu dans V.
      Après, la bibliothèque d'interface graphique n'est vraiment pas du tout au point.

      J'ai essayé de faire un petit logiciel d'édition de sprite avec il y a quelques année et je m'étais bien cassé les dents.

      Y a de bonnes idées, mais pas assez de réflexion sur la notion d'état de la vue, ce qui aujourd'hui est dommage quand on voit les apports de la programmation réactive.

      Récemment j'ai découvert une mini lib Javascript qui permet de faire un peu la même chose que du React mais tellement plus simplement. J'adorerai voir ce genre de concept porté dans d'autres langages comme V par exemple.

  • # if sans parenthèses

    Posté par  (site web personnel) . Évalué à 5.

    Je dois avoir l'esprit tordu (bon, ok c'est une certitude) mais je trouve que ce passage va être nominé dans catégorie 'le détail mis en avant qui ne sert à rien'…
    Les parenthèses sont souvent obligatoires car séparant la condition du code à exécuter. Dans le cas de V, ça semble être l'accolade délimitant le code qui est obligatoire, rendant inutiles les parenthèses pour la condition.

    Parce que je ne vois pas trop la différence entre

    if (condition) do_toto();

    et

    if condition { do_toto() };

    Du coup, à par satisfaire les accoladophiles parenthèsophobes, je ne vois pas trop la killer feature ….

    • [^] # Re: if sans parenthèses

      Posté par  . Évalué à 5.

      Ce n'est pas une killer feature, juste un choix de syntaxe issue de Go.

      Disons que c'est plus cohérent, car l'exemple sans accolade ne fonctionne que si on a qu'une seule instruction.

    • [^] # Re: if sans parenthèses

      Posté par  (site web personnel) . Évalué à 3.

      Le choix de forcer l'usage des accolades dans Rust est d'éviter des bogues dus à l'ajout d'une nouvelle instruction à la suite de la première, sans se rendre compte que cette seconde instruction sera exécutée inconditionnellement car elle n'est pas entourée d'accolades (cf. une discussion sur rust-lang). Peut-être que c'est aussi la raison qui a guidé ce choix en Go ?

      Avec l'outil de formatage automatique, l'erreur va être évidente au programmeur car la seconde instruction sera réindentée et donc ne semblera plus être dans un bloc qui n'existe pas. Dans ce cas, l'argument est moins convaincant.

    • [^] # Re: if sans parenthèses

      Posté par  . Évalué à 10.

      à par[t] satisfaire les accoladophiles parenthèsophobes, je ne vois pas trop

      L'absence d'accolade autour d'une clause then - car d'une seule ligne - est la source de nombreux bugs en C. Imposer les accolades est donc une bonne chose IMHO.

      Si tu imposes les accolades, les parenthèses autour de la condition ne sont donc plus nécessaire puisque le parsing n'est plus ambiguë : la condition est ce qu'il y a entre le if et le prochain {.

  • # Sather

    Posté par  (site web personnel) . Évalué à 3. Dernière modification le 05 septembre 2023 à 16:04.

    L'auteur devrait regarder du côté de Sather https://fr.wikipedia.org/wiki/Sather. Il y avait dans ce langage tout ce qui était amusant avec des API chouettes. Héritage de structure et inclusion de code, interface… Une gestion contravariante (donc sure) des paramètres et surtout des iterators (en pratique comme les coroutines) avec la notion de yield et de paramètres once (évalué juste la 1er fois et non les suivantes). Je n'ai jamais revu un autre langage avec des itérateurs aussi esthétiques et pratiques.

    Autre chose amusante en Sather, on pouvait ajouter un noeud dans l'arbre des classes après coup. Ainsi une classe système pouvait dériver d'une classe que l'utilisateur définie et que le concepteur n'avait pas pensé. Rare sont les langages à le permettre or toutes les arbres de classe sont imparfaits et nécessitent un jour de définir une classe virtuelle maître à différent endroit pour son utilisation spécifique.

    • [^] # Re: Sather

      Posté par  . Évalué à 2.

      Sather avait un GC alors que là on parle d'un C-like.

      • [^] # Re: Sather

        Posté par  . Évalué à 2.

        Non V a utilise aussi un ramasse miette. Il cherche aussi à développer d'autres méthodes de gestion automatique de la mémoire, mais ce n'est pas un langage où l'on est responsable de la mémoire.

        • [^] # Re: Sather

          Posté par  . Évalué à 3.

          Ah? Dans ce cas je n'en vois pas l'intérêt D est bien + mature..

    • [^] # Re: Sather

      Posté par  . Évalué à 3.

      OMG que c'est laid ! 🤮

      Oui je sais, les goûts les couleurs. Mais ce mélange de syntaxe Pascal (end) et C (;), ça me fout la gerbe.

      Et puis à quoi bon un débat sur les langages de prog si on peut pas parler de la beauté de la syntaxe 😉

      • [^] # Re: Sather

        Posté par  . Évalué à 3. Dernière modification le 06 septembre 2023 à 15:34.

        Mais ce mélange de syntaxe Pascal (end) et C (;)

        Pascal a aussi des ;

        • [^] # Re: Sather

          Posté par  (site web personnel, Mastodon) . Évalué à 2.

          Tout à fait, il n'y a pas de mélange de genres. L'exemple

          class HELLO_WORLD is
           main is 
            #OUT+"Hello World\n"; 
           end; 
          end;
          -- etc
          

          se traduit en (plus verbeux pour ce cas simple)

          type 
           HELLO_WORLD = object
             procedure main;
           end;
           procedure HELLO_WORLD.main;
           begin
            write('Hello World\n');
           end;
          { etc }

          Bien noter le ; sauf après : type, begin, var, les commentaires.
          Et en prime Pascal a le fameux end. final…

          “It is seldom that liberty of any kind is lost all at once.” ― David Hume

      • [^] # Re: Sather

        Posté par  (site web personnel, Mastodon) . Évalué à 2.

        J'aurais cru que le marqueur est end; (une seule instruction et non deux), mais en regardant d'autres source je comprends juste que ; est requis …sauf
        - fin de ligne de commentaire introduit par --
        - après les mots clé is, then, else.
        Ce dernier point que j'ai d'abord vu comme des exceptions (donc surprenant) fait sens quand on sait qu'une instruction ne peut pas se terminer abruptement dessus…

        scale_x(x:INT):INT is
         if x > 0 then    return 15;
         else  raise "An error occurred!";  end;
        end;
        

        (exemple en 2.4 du manuel sur l'essentiel…) peut s'étaler comme suit (qui t'a choqué)

        scale_x(x:INT):INT is
         if x > 0 then
            return 15;
         else
            raise "An error occurred!";
         end;
        end;
        

        ou même se compacter comme suit (je suppose, mais le is if est piquant)

        scale_x(x:INT):INT is if x > 0 then    return 15;
         else  raise "An error occurred!";  end; end;
        

        “It is seldom that liberty of any kind is lost all at once.” ― David Hume

    • [^] # Re: Sather

      Posté par  (site web personnel) . Évalué à 2.

      Pour les curieux qui voudraient voir à quoi ça ressemble:
      la page du manuel Sather sur les itérateurs

      Le once est à la section 3.2.4. Lisez ce qu'il y a avant pour mieux comprendre.

      Je ne suis pas habitué à ce type de syntaxe donc ça doit jouer sur le fait que je ne trouve pas ça très limpide.

    • [^] # Re: Sather

      Posté par  . Évalué à 2.

      Merci pour la découverte (ou peut-être redécouverte je ne sais plus :)) de ce langage.
      Souvent, les bonnes idées viennent de langages académiques qui n'ont pas percés (par exemple la notion de channel de Go vient de Limbo il me semble).

  • # go good enough

    Posté par  (site web personnel) . Évalué à 5.

    J'ai du mal à voir un vrai avantage par rapport à Go.

    Même en tant que Go light, il y a déjà Tinygo…

    Le post ci-dessus est une grosse connerie, ne le lisez pas sérieusement.

    • [^] # Re: go good enough

      Posté par  . Évalué à 4.

      Oui et non.

      L'avantage serait potentiellement d'avoir du C pour lequel beaucoup de logiciels existent (analyse formelle entre autre) ou pour un besoin client qui demanderait du C (tu écris du V et lui livre du C (plus productif).

      Mais oui il n'y a pas d'avantages majeur au V (performance équivalentes et compilation plus lente) qui permette de lui donner une grosse communauté à terme et beaucoup "d'inconvénient".
      Cela reste un projet intéressant pour de la recherche ou pour le fun et utile dans de rare cas.

    • [^] # Re: go good enough

      Posté par  (site web personnel) . Évalué à 5.

      J'avais un peu suivi ce langage au début, et l'avantage principal (promis) c'était d'avoir l'expressivité du go mais avec des variables immutables par défaut (ce qui apporte plus de garanties) et sans garbage collector (ce qui améliore les perfs).

      Malheureusement j'ai l'impression après la lecture de cette dépêche qu'une bonne partie du langage est encore à l'état de promesses.

      Il existe deux catégories de gens : ceux qui divisent les gens en deux catégories et les autres.

      • [^] # Re: go good enough

        Posté par  . Évalué à 5. Dernière modification le 07 septembre 2023 à 09:22.

        Malheureusement j'ai l'impression après la lecture de cette dépêche qu'une bonne partie du langage est encore à l'état de promesses.

        Oui, il y a un "déjà là" fonctionnelle et tout de même plutôt abouti, mais l'écart entre ce qui est annoncé et la réalité est assez important.
        C'est pour moi la plus grosse erreur des concepteurs du langage : de mettre en avant certains aspects qui sont encore très très expérimentaux (gestion de la mémoire, ui etc.).

  • # Une gestion d’erreur moderne ?

    Posté par  (site web personnel) . Évalué à 5.

    Effectivement les exceptions qui ne sont pas rattrapées et remontent implicitement la stack trace ne sont pas une solution satisfaisante. Mais je ne trouve pas que la solution proposée soit plus moderne. Ça ressemble plus à du sucre syntaxique avec des conventions implicites, ce qui le rendent moins lisible pour un novice, ce qui est un mauvais point selon moi.

    C'est dommage parce que le langage s'inspire du go dont j'aime beaucoup la gestion d'erreur :
    * c'est un type comme un autre, il n'y a pas de notion d'erreur dans la syntaxe du langage, c'est une convention, mais une convention explicite
    * la gestion des erreurs est elle aussi explicite : si on veut tester si une erreur a été retournée, on teste si l'erreur est différente de nil. Il n'y a pas de "if" magique qui teste implicitement ce cas. Un "if" fonctionne uniquement avec un booléen.

    Ça donne du code plutôt verbeux j'en conviens, mais tout est explicite. Et le fait que ça ne soit pas une feature de la syntaxe du langage fait une feature de moins à apprendre.

    Le problème restant, et commun à toutes ces gestions d'erreur, c'est la question de comment savoir quelles erreurs peut retourner une fonction. Pour l'instant peu importe le modèle, je n'ai jamais rien vu de satisfaisant.

    PS : je suis allé voir la gestion des erreurs en zig, c'est de l'implicite couplé à la gestion implicite des optional, ça me file la nausée ^

    Il existe deux catégories de gens : ceux qui divisent les gens en deux catégories et les autres.

    • [^] # Re: Une gestion d’erreur moderne ?

      Posté par  (site web personnel) . Évalué à 8. Dernière modification le 06 septembre 2023 à 14:32.

      Je trouve la gestion d'erreur de Go atroce, pour ne pas dire inexistante.

      Le langage ne te force pas à la traiter, rien ne t'empêche d'écrire :

      res, _ := foo()

      Il peut quand même y avoir des données dans le résultat, même si err est nil :

      func foo() (int, error) {
        return 42, errors.New("oops")
      }

      Dans ce cas, on fait quoi de res ?

      Il faut utiliser des modules tiers pour garder les informations qu'une stacktrace d'exception a par défaut, car :

      res, err := foo()
      if err != nil {
        return whatever, err
      }

      Ici on pert complètement l'information, la bonne pratique c'est d'encapsuler l'erreur dans une nouvelle, en utilisant par exemple le module xerrors.

      Le type error est concrètement juste une chaîne de caractères, sans module tiers tel que xerrors ou autre, impossible de déterminer le type de l'erreur, alors qu'avec une exception, on peut au moins faire du pattern matching sur son type.


      Au final, je préfère un type monadique, comme std::expected<T, E> en C++ ou Result<T, E> en Rust.

      Ici:

      • on a bien une valeur OU une erreur
      • on est obligé de traiter l'erreur
      • on peut faire du pattern matching sur le type de l'erreur et sa valeur
      • on sait quel type d'erreur une fonction peut retourner

      Les exceptions pourraient être du sucre syntaxique pour un tel type monadique. D'ailleurs, l'opérateur ? en Rust ressemble beaucoup à ça :

      fn foo() -> Result<T, E1> {
        // ...
      }
      
      fn bar() -> Result<T, E2> {
        let val = foo()?;
        // si foo retourne Result::Err(E1),
        // alors bar retourne Result::Err(E2) a condition que E2 implémente From<E1>
        // si l'implémentation n'est pas trouvée --> erreur de compilation
      }

      C'est tout de suite plus propre.


      TL;DR: Les gens qui vantent les mérites de la gestion d'erreur en Go, je les comprends pas :(

      https://link-society.com - https://kubirds.com

      • [^] # Re: Une gestion d’erreur moderne ?

        Posté par  (site web personnel) . Évalué à 6.

        Le type error est concrètement juste une chaîne de caractères

        Non, c'est une interface :

        type error interface {
            Error() string
        }

        Source : https://go.dev/doc/effective_go#errors

        En pratique, les erreurs sont souvent des chaînes de caractères, mais on peut trouver d'autres types d'erreurs. Par exemple, la bibliothèque standard a un type PathError dans io/fs défini ainsi :

        type PathError struct {
            Op   string
            Path string
            Err  error
        }

        Source : https://pkg.go.dev/io/fs#PathError

        sans module tiers tel que xerrors ou autre, impossible de déterminer le type de l'erreur

        De nos jours, on peut faire ça avec le module errors fourni avec la bibliothèque standard : https://pkg.go.dev/errors

        Au final, je préfère un type monadique, comme std::expected en C++ ou Result en Rust.

        Ici : […] on est obligé de traiter l'erreur […]

        Est-ce que faire un unwrap, ça compte vraiment comme traiter l'erreur ? Mon avis personnel est que ce n'est ni mieux ni pire que res, _ := foo(). Dans les deux cas, on ignore explicitement l'erreur sans la traiter.

        Perso, je trouve la gestion des erreurs en Go assez bof, mais pas atroce non plus.

        • [^] # Re: Une gestion d’erreur moderne ?

          Posté par  (site web personnel) . Évalué à 4.

          Non, c'est une interface

          My bad, je le savais en plus, mais j'ai vu tellement de codebases Go ou c'était juste des strings que j'ai fait un raccourci un peu trop rapide.

          Est-ce que faire un unwrap, ça compte vraiment comme traiter l'erreur ?

          Oui car unwrap sur un Result::Err(E) ça panic!. Alors que ne pas faire de if err != nil en Go, le programme continu comme si de rien n'était.

          Dans les deux cas, on ignore explicitement l'erreur sans la traiter.

          Ben non :

          func foo() (something, error) {
            a, err := bar()
            // pas de if err != nil
          
            // beaucoup de code
          }

          La c'est pas explicite du tout.

          Alors qu'en Rust :

          if let Ok(a) = bar() {
            // ...
          }
          
          // ou
          
          match bar() {
            Ok(a) => { /* ... */ },
            _ => {}
          }

          En gros :

          • en Go, tu dois ajouter du code pour traiter l'erreur
          • en Rust, tu dois ajouter du code pour ignorer l'erreur

          Perso, je trouve la gestion des erreurs en Go assez bof, mais pas atroce non plus.

          Chacun ses goûts. Pour ma part, je défend l'idée que "fournir un type/interface 'error'" ce n'est pas de la gestion d'erreur.

          En fait, je ne vois même pas de différence avec le C dans ce cas là :

          int foo(); // retourne 0 en cas de succès, -1 en cas d'erreur

          Au moins, la GLib (pas gnu libc, mais bien GLib de GTK), on a GError et compagnie :

          bool foo(GError **err);
          // true en cas de succès, false en cas d'erreur
          // si erreur, et que err != NULL, on propage l'erreur dans *err

          Verbeux, peu pratique, mais déjà un peu mieux qu'un int.

          https://link-society.com - https://kubirds.com

      • [^] # Re: Une gestion d’erreur moderne ?

        Posté par  . Évalué à 4.

        Je trouve que la confusion vient du terme "error" qu'on aurait plutôt du appeler "status" ou quelque chose comme ça. C'est une simple information, pas forcément une erreur.
        Par exemple io.EOF, sql.ErrNoRows, j'ai des handlers peuvent renvoyer une "erreur" de type Redirect etc.
        Du coup c'est une valeur retournée comme une autre et donc traitée comme n'importe quelle valeur.
        Comme Go est au départ utilisé pour du réseau le fait que cette "erreur" doit pouvoir renvoyer un string permet de faire transiter tout ça facilement.
        C'est aussi comme les retours de commandes unix qui renvoient 0 1 -1…
        Y a un copain qui m'a dit récemment, mais tu gérais les erreurs aussi comme ça en Python !

        Les véritables erreurs c'est plutôt les panics qu'on rattrape avec recover.

    • [^] # Re: Une gestion d’erreur moderne ?

      Posté par  . Évalué à 2.

      Ça donne du code plutôt verbeux j'en conviens, mais tout est explicite. Et le fait que ça ne soit pas une feature de la syntaxe du langage fait une feature de moins à apprendre.

      Ce point est à mon avis important.

      La force de Go est d'être épuré au maximum (je crois qu'il n'y a que 18 mots clés) et donc de reduire au maximum l'effort cognitif pour appréhender le langage : pas trop de nouveaux concepts (bon, les coroutines et les channels c'est déjà pas mal) et un syntaxe claire et explicite.

      Là où V se démarque, c'est justement dans ces petits choix syntaxiques qui apportent beaucoup de fluidités à l'écriture. Alors c'est vrai, c'est pas toujours très explicite (les variables "magiques" it, err etc.), mais ça rend le code assez léger.

      Il y a un aspect moins formel que l'on retrouve généralement plus dans des langages comme python et javascript. Je viens personnellement du C++ à la base et j'ai rapidement été écœuré par le côté ésotérique de la syntaxe et les lenteurs de compilation, puis après j'ai plus codé dans des langages de scripts et ce qui m'écœure aujourd'hui c'est le manque de compilation en amont, le typage faible.

      V tente de continué dans la lancé de Go, tout en allant peut-être plus loin dans l’expressivité du code et j'aime bien le résultat.

      Après, je code "peu" en V, je lis beaucoup dessus, suit son développement de près, mais ça reste au stade d'expérimentation. Je comprends à 100% que l'on ne veuille pas s'y mettre.

      PS : je suis allé voir la gestion des erreurs en zig, c'est de l'implicite couplé à la gestion implicite des optional, ça me file la nausée ^

      Ah c'est assez étonnant comme remarque, car Zig est justement connu pour être explicite. Dans ce sens où il n'y a pas d'appel de fonction caché, pas d'allocation implicite.
      Je trouve le choix d'intégré la gestion d'erreur comme un élément de la syntaxe plutôt judicieux. Cela limite la verbosité du code et rend la gestion d'erreur plus confortable.

      J'ai tout de même l'impression qu'avec ces notions de Optional/Result on arrive à une sorte de consensus (ou plutôt de convergence) où beaucoup de langages récents ont fait le choix de traiter les erreurs et les retours de fonction de cette manière.

      • [^] # Re: Une gestion d’erreur moderne ?

        Posté par  . Évalué à -3.

        If, Goto, def pour la déclaration d'objets (variables, fonctions). C'est tout ce qui compte. On fait bien plus qu'avec n'importe quel langage soit disant moderne.

        Pour Go y'en a un peu plus :

        https://www.gladir.com/CODER/GO/reservedword.htm

        Faudrait aussi prendre en compte les éléments syntaxique pour bien faire (délimiteurs de blocs, affectation, le ? en C, etc.), car ils ont une sémantique. Le système de type (trop de pauvreté n'aide pas) : faute de formation théorique poussée ça tient rarement la route en informatique (au moins les langages à la C ne trichent pas en assumant leur orientation bas niveau). Le système de type ne devrait même pas être imposé par le langage mais proposé en lib standard.

        C'est souvent un compromis. Langage complexe ~ code simple vs. langage simple ~ code complexe.

  • # Quelques notes

    Posté par  . Évalué à 2.

    Notes:
    L’installation (compilation) se poursuit sans problèmes et le temps dependent de la performance du système , j’ai essayé :
    Ubuntu 22 x86_64 temps ~2-5 secondes
    Ubuntu 20 aarch64(Samsung/proot) temps quelques bonnes minutes, mais toujours sans faute.

    Attention!
    a.p.d. La version 0.4.0 les annotations sont balisées différemment ( par @[] )

    il faut dire que l’utilisation des []
    est fort surchargée :
    - pour accéder l’index d’une array
    - pour accéder la clé d’un map.
    - pour la définition des paramètres génériques
    - pour faire référence à des variables externes au contexte d’appel d’une expression lambda.
    - jusqu’à v0.4.0 pour définir des annotations.

    • [^] # Re: Quelques notes

      Posté par  . Évalué à 3.

      L’installation (compilation) se poursuit sans problèmes et le temps dependent de la performance du système , j’ai essayé :

      Il faut savoir que la compilation du binaire v se fait via gcc par défaut. En revanche la compilation des binaire issue de fichier écrit en v via la commande v ou v run se fait par défaut avec tcc et est extrêmement rapide. Le fait que tout le code se retrouve dans un fichier .c doit aider un peu je pense.

      a.p.d. La version 0.4.0 les annotations sont balisées différemment ( par @[] )

      Tu veux dire les attributs (genre [inline] etc.) non ?
      Je viens de le voir dans la release note, dommage que la doc ne soit pas mise à jour en conséquence. Bon en même temps c'est en 0.4.1 qui est sortie la semaine dernière.

  • # Génial

    Posté par  . Évalué à 3.

    C’est un langage qui promet beaucoup.

    Par difference au C, les macros sont matérialisés par du code écrit en V, qui est interprété au pas de compilation. La réflexion sur les metadonnees du code se fait ainsi toujours à l’étape de compilation.
    Ça été super facile à écrire un Serializer pour json ou xml ( a la base quelques dizaines de lignes du code sans traiter les erreurs d’une manière très profonde), ce qui démontre la puissance de ce mécanisme.

    La librairie standard est bien garnie avec le tout nécessaire à la programmation système ou bien pour les applications graphiques ou web.

    il existe un gestionnaire des paquets applicatifs (la commande v install ) qui sert à installer des paquets V dans le système ou de publier des modules V sur https://vpm.vlang.io/.

    le projet est financé et extrêmement actif ( des ordres de magnitude par rapport à des autres projets OSS )

    • [^] # Re: Génial

      Posté par  . Évalué à 2.

      Je suis aussi assez enthousiaste par rapport à ce langage.

      Par difference au C, les macros sont matérialisés par du code écrit en V, qui est interprété au pas de compilation.

      C'est un des aspect du langage que je n'aborde pas dans la dépêche, mais il est en effet possible d'avoir du code qui s'exécute à la compilation, un peu à l'instar de comptime en Zig mais beaucoup moins générique. Ça reste néanmoins un outil de méta programmation assez puissant et un moyen de remplacer l'usage des maccros dans certains cas (cross compilation). La notion de file embedded est particulièrement intéressante je trouve. Voir la doc à ce sujet. Ce qui est marrant c'est que Zig a exclus cette possibilité dans comptime, car ce n'est pas cross platform.

      le projet est financé

      As-tu des informations la dessus ? J'ai l'impression que la situation est moins claire que pour d'autres projets qui sont sous l'égide de fondation par exemple.

  • # decouverte

    Posté par  . Évalué à 1. Dernière modification le 12 septembre 2023 à 07:46.

    Salut
    Decouverte de ce langage depuis la depêche du 05/09
    Vraiment sympathique à pratiquer. Les exemples sont plus qu'utile pour s'y mettre
    Langage reste très technique; Heureusement qu'il y a des commentaires içi

    J'espère qu'au fure et à mesure qu'il sera à nouveau découvert par certains, comme moi, on pourra partager de nouveaux codes

    sinon le fait que ce langage soit open source, permet à la lecture des en tetes ( par exemple string.js.v ), d'optimiser l'utilisation des méthodes …

    dans les baba que je n'ai pas vu transpirer dans les commentaires :

    déclaration d'une fonction avec un retour :

    fn mafonction(variable1 string, variable2 string) String
    {
       return variable1 == variable2
    }
    
    ouvrir un fichier pour une lecture ligne par ligne
    fn read_file(file string, cap int) []string {
        mut lines := []string{}
        mut verbeux := true
        mut ligne := ""
        mut type_first := false // @COMMENTAIRE declaration d une variable de type bool
        mut f := os.open(file) or { panic(err) }
        defer {
            f.close()
        }
        mut r := io.new_buffered_reader(reader: f, cap: cap)
        for {
            // @COMMENTAIRE declaration d une constante
            l := r.read_line() or { break }
            lines << l
            // @COMMENTAIRE manipulation de chaine de caractere ( voir string.js.v pour d autres methode )
            if ( l.starts_with('FIND') )
            {
                // @COMMENTAIRE  encore une manipulation de chaine de caractere ne garde que les caracteres après le mot clef
                println(l.after('FIND'))
            }
            else
            {
                println(l)
            }
            }
        }
    }
    
    fn test_file_reader() {
            cap := 10000
            lines := read_file('monfichier.txt', cap)
            // @COMMENTAIRE contenu du fichier monfichier.txt
            // ceci est un fichier
            // FIND un element parmis tant d autre
            // le code execution.v affichera toute les lignes entierement, sauf la ligne precedente ou le mot FIND n apparaitre pas
    }
    
    fn main()
    {
                //@COMMENTAIRE lit le fichier monfichier.txt et affiche les lignes selon une synthaxe programmee
                test_file_reader()
    }
    

    Pour generer une GUI facile, partir du code user.v (manque de commentaires mais ont s'y retrouve)

    Bref avec un peu d'huile de coude facile de s'y mettre

  • # sssssssss....

    Posté par  . Évalué à 3.

    Avec un nom comme 'V', il tente vraiment de nous faire croire que c'est pas un coup des pythons ?

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.