Langages


h1 10/19/2006 09:37:00 AM

Il y a quelques semaines je tombais sur un os en Javascript, et je postais ici-même mon étonnement et ma déception. J'en ai depuis appris un peu plus, et en ai surtout discuté avec mon père, et sur LtU où j'essaie de justifier ma déception... Tout cela appelle à une longue suite, sur JavaScript en particulier et les langages de programmation en général.

Tout d'abord un exemple plus simple révélant le problème dans son ensemble avec Javascript:
var a = 0;
function f() {
// La variable locale "a"
// est déclarée pour le corps de la fonction,
// dès lors qu'une déclaration de "a" a lieu où
// que ce soit dans la fonction, 5 lignes plus bas ici.
a = 1 ;
// Ce n'est donc pas le "a" global qui a été modifié ici.
// Mais pour le savoir il faut lire deux lignes plus bas.
if (false) {
var a = 2 ;
...
}
}
// Ici, "a" vaut toujours 0.

Les concepteurs de JavaScript avaient des priorités et ont préféré ne pas s'embêter avec des portées plus fines que la fonction. C'est un choix qui a aussi été fait dans les premières standardisations du C, mais dans ce cas la déclaration d'une variable ailleurs qu'en début de fonction est tout bonnement interdite, rejetée à la compilation. Dans des versions récentes du C, en C++ ou Java, les déclarations de variable sont autorisées n'importe où et leur portée est restreinte au bloc englobant. En OCaml ou SML, la déclaration de variable let VAR = EXPR in BODY forme un bloc à elle seule, et la portée de VAR est restreinte à BODY.

En gros, j'ai commencer à coder en C, puis ai passé des années avec OCaml. Dans les deux cas j'ai été habituée à des portées de bloc. Avec OCaml on en fait un usage intensif, par exemple en émulant les variables static de C en déclarant une variable locale visible uniquement depuis une certaine fonction:
let compteur =
let n = ref 0 in
fun () -> incr n ; !n
(* "n" invisible après la définition de "compteur" *)

J'ai aussi passé un été à programmer en Emacs Lisp. Dans ce langage la liaison des variables est dynamique. Cela signifie que si une fonction fait référence à une variable n, elle fait référence à la variable n dans le contexte de son exécution (dynamique) et non pas dans le contexte de sa définition (statique). Cela permet certains bricolages mais la plupart du temps cela amène à de mauvaises surprises. Avec l'habitude on évite les mauvaises surprises en évitant de construire des fonctions dans un contexte pour les exécuter dans un autre (c'est dommage), ou on force l'évaluation des variables en question lors de la construction de la fonction (c'est pénible).

De temps en temps j'ai été amené à pratiquer JavaScript, car il n'y a pas le choix pour scripter côté client sur le web. Il m'a aussi parfois été utile d'utiliser Python, pour développer rapidement un petit outil grâce aux innombrables librairies disponibles. Dans les deux cas j'ai été heureux de constater que les fonctions sont des valeurs comme les autres et que la liaison des variables est statique -- on dit aussi que la portée est statique/lexicale. Je me suis réjoui à l'idée que les langages de script se modernisaient, et que j'allais pouvoir m'exprimer librement et richement. J'ai dit autour de moi que JavaScript c'est bien. Et je connais un mec qui utilise JavaScript comme son langage de tous les jours, et qui a voulu participer au concours de prog de l'ICFP avec, parce que en gros c'est statique, propre, fonctionnel.

Eh bien aujourd'hui je modère ce jugement. JavaScript n'est pas si riche, il ne me plait pas tant que ça. Et en plus il autorise des déclarations de variable n'importe où, ce qui induit en erreur les habitués d'autres langages -- et nous sommes nombreux.

Pourquoi poster ce genre de notes ? J'espère sensibiliser autour de moi ceux qui ne le sont pas encore: Il y a de nombreuses subtilités dans la conception d'un langage, et les langages les plus populaires ne sont pas ceux qui ont résolu ces subtilités au mieux. Sur la question de la portée des variables, de nombreux langages offrent une portée statique, plus expressive et moins surprenante d'utilisation. D'un point de vue technique c'est donc possible, et depuis longtemps. Le léger coût que cela a me semble être le genre de chose qu'on oublie volontiers aujourd'hui, dans la mouvance des langages haut-niveau. Le typage est une autre caractéristique importante des langages, et beaucoup de gens n'ont pas idée de ce qui est possible, par exemple en Caml où le typage est statique mais toujours inféré et généralisé automatiquement -- cf mon post récent. Tant que de nouveaux langages, au demeurant attractifs et aux nombreuses qualités, seront développés en ignorant/rejettant ces solutions, elles resteront méconnues. C'est dommage, car les développeurs passent à côté d'une sémantique plus claire, de plus d'erreurs détectées automatiquement dès la compilation, et d'une exécution plus rapide. A défaut de créer moi-même un langage simple et riche, j'aimerais essayer de pousser les autres à aller dans cette direction, dans la mesure de mes possibilités très limitées.

Je conclus sur l'exemple du compteur en Javascript, qu'il nous faut modifier ainsi:
function cree_compteur() {
var n = 0 ;
return (function () { return ++n }) ;
}
var compteur = cree_compteur() ;

C'est pas si dur, mais il vaut mieux le savoir. Fini pour aujourd'hui, je ne traite pas de l'absence de portée de bloc en Python...

PS: J'oubliais de pointer la proposition d'ajouter à JavaScript 2 un nouvel opérateur de déclaration de variables à la portée plus stricte. Pour des raisons de compatibilité, le nouveau let ne remplacera pas l'ancien var.

0 commentaires:

Un commentaire ?

< Accueil