Quand "let" devient "var"
Écrire un langage, c'est aussi faire des choix. Et parfois des mauvais, qui entraînent des modifications.
Décider de changer celui-ci a été difficile, car est un peu au coeur du système. Mais finalement assez facile à mettre en oeuvre...
Et donc, on déclare maintenant une variable par var et non plus let.
Une variable ?
Une variable en programmation est une boîte étiquetée qui sert à stocker une valeur qu'il est possible d'utiliser, modifier et réutiliser plus tard dans le code.
Elle est définie par son nom, sa valeur (stockée en mémoire) et son type (nombre, chaine...).
Déclarer une variable ?
Si l'on compare la manière de déclarer une variable dans différents langages, plusieurs candidats sont sur les rangs... (syntaxes simplifiées):
let x = 5 // rust
let x = 5 // typescript (js)
var x : integer // pascal
var x = 5 // go
var x = 5 // kotlin
var x = 5 // swift
Dim x As Integer // Visual Basic (dimension)
local x = 5 // lua: portée locale
Le JavaScript apporte un distingo entre let et var.
var x = 5 // déclaration simple de variable
let x = 5 // variable locale, dans un scope donné
A notéer qu'en JavaScript, let vient des let-expressions des langages fonctionnels et signifie : introduire des noms locaux valables dans un certain scope (on en reparle). Le mot let vient du verbe anglais “to let” (permettre, laisser), mais surtout d’une construction très ancienne appelée let-expression, présente dans plusieurs langages fonctionnels (Lisp, Haskell...) (let ((x 5) (y 3)) (+ x y)) - langage connu pour aimer les parenthèses...
Dans la famille C on doit en outre préciser le type
int x = 5 // C, C++, C#,
Certains langages compilés (go, dart...) permettent plusieurs syntaxes
- typage explicite
- inférece de type (déduction statique à la compilation, selon sa valeur)
var name string = "RichnouLang" // avec typage
name := "RichnouLang" // inférence de type, déduit
Dans certains langages on crée la variable directement sans mots clefs, on va les créer par affectation, on parlera alors de typage dynamique, qui sera vérifié à l'exécution. Souvent, une variable peut alors changer de type si on en change la valeur.
x = 5 // Python
x = 5 // Ruby
$x = 5 // PHP
x = 5 // MATLAB
x=5 // Shell
Typage d'une variable
Concernant le typage, plusieurs points sont à considérer. Petit résumé:
- Force du typage
- Fort : interdiction de mélanger les types sans conversion explicite (
"5" + 3= erreur en Python, Go, Rust) - Faible : conversions implicites agressives (
"5" + 3va donner8en PHP mais"53"en JS)
- Fort : interdiction de mélanger les types sans conversion explicite (
- Moment de vérification
- Statique : vérification des types à la compilation, avant exécution (Go, Java, Rust)
- Dynamique : vérification des types à l'exécution, le type change avec la valeur (Python, Ruby, PHP)
- Mode de déclaration des types
- Explicite : le programmeur écrit le type (
var name string,String name) - Inférence : déduction automatique du type à la compilation sans l'écrire explicitement (Go, Dart, Kotlin...)
- Implicite : aucun type déclaré, attaché à la valeur (name = "txt" en Python)
- Explicite : le programmeur écrit le type (
Si on ne précise pas de valeur, certains langages affectent une valeur par défaut, là où d'autres utiliserons une valeur spéciale (null ou undefined)
var name string // go = "" (valeur par défaut)
let message // js = undefined
String text; // java = null
Une variable n'est pas forcément utilisable partout. On parle de sa portée (ou scope en anglais)
- Locale : déclarée dans une fonction, accessible uniquement dans cette fonction
- Globale : déclarée hors fonction, accessible depuis tout le programme (selon le langage)
- De bloc : déclarée dans un bloc (if, for, etc.), accessible uniquement dans ce bloc
Et en RichnouLang ?
La déclaration des variables se faisait au tout début via let (je venais de faire du JavaScript) mais j'ai fait une refonte pour utiliser var qui me parait plus naturel, plus pédagogique.
Le typage est déterminé par inférence, selon sa valeur, avec un nombre limité de types:
- nombre (décimal obligatoirement, pouvant donner des valeurs
1.000000, les zéros à la fin étant retirés en cas d'affichage donc1) - chaine de caractère entre guillemets (utiliser des
'génère uneFATAL ERROR) - booléen (
TRUEouFALSE) - sans valeur, la variable vaudra
NULL
var a = 5 ; console(a); // 5.000000 - 5 s'affiche
var b = "4"; console(b); // "4"
var c ; console(c); // NULL
var d = false ; console(d); // FALSE
Même si je doute que ce soit une bonne idée, RichnouLang ne s'oppose pas à la concaténation d'un nombre et d'une chaine, ou d'un booléen, mais attention aux zéros (ce comportement sera peut-être changé)...
var a = 5;
var b = "7";
var c = a+b; console(c); // 5.0000007
var d = a+true; console(d); // 5
var e = b+true; console(e); // 7TRUE
Il est possible d'utiliser des listes, ou array (tableau de valeurs), à plusieurs dimensions.
var liste = [10, 11];
console( liste[0] );
var matrice2D = [[1,2,3],[4,5,6]];
var matrice3D = [[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]];
var matrice4D = [[[[1,2],[3,4]],[[5,6],[7,8]]],[[[9,10],[11,12]],[[13,14],[15,16]]]];
var matrice5D = [[[[[1],[2]],[[3],[4]]],[[[5],[6]],[[7],[8]]]],[[[[9],[10]],[[11],[12]]],[[[13],[14]],[[15],[16]]]]];
var matrice6D = [[[[[[1]],[[2]]],[[[3]],[[4]]]],[[[[5]],[[6]]],[[[7]],[[8]]]]],[[[[[9]],[[10]]],[[[11]],[[12]]]],[[[[13]],[[14]]],[[[15]],[[16]]]]]];
console(matrice4D[0][0][0][0]);
console(matrice6D[0][1][2][3][4][5]); // index out of bounds !
A noter que de nombreuses fonctions retournent un flat array, c'est à dire une liste simple mais qui est en fait une succession de valeurs, de type : [x1,y1, x2,y2, x3,y3 ...] mais un article spécifique leur sera consacré.
Il est également possible d'utiliser des dictionnaires (structure clé-valeur), éventuellement imbriqués sur plusieurs niveaux.
var pt = {x:12, y: 20};
console(pt); // {x: 12.000000, y: 20.000000}
console(pt.x); // 12
var pt3D = {
color: [255, 0, 0] ,
position: { x: 12, y: 20 }
};
console(pt3D);
console(pt3D.color);
console(pt3D.color[0]);
console(pt3D.position);
// ATTENTION : les objets imbriqués ne sont pas encore correctement gérés
console(pt3D.position.x); // erreur...
// il faut passer par une variable intermédiaire
var pos = pt3D.position;
console(pos.x);
Au niveau du scope on est sur du classique:
- variable globale, utilisable "partout" (y compris dans les blocs)
- variable locale, utilisable uniquement dans les blocs
- impossible de redéfinir une globale à l'intérieur d'un bloc
- les "compteurs" de boucle utilisable dans la boucle
var x = 50; // globale
def hello() {
var y = 12; // locale à la fonction
console(x, y); // x accessible
}
hello();
// console(y); // ERROR n'existe pas en dehors de la fonction
for (var i=0; i<3; i++) {
var j = i*2; // locale à la boucle
for (var k=0; k<j; k++) {
console(x, i, j, k); // on est bon !
}
// var x = 12 ; // ERROR 'x' est globale, mais ne peut être locale
// console(k); // ERROR n'existe pas en dehors de la boucle
}
// console(j); // ERROR n'existe pas en dehors de la boucle