Une critique du Lua

Laurent proposait dans l’un de ses articles une critique d’une vingtaine de langages de programmation. Parmi ceux-ci, le Lua était présenté très brièvement. Je propose ici de développer un peu sur ce langage.

Il s’agit donc d’un langage particulièrement léger, qui s’intègre très facilement dans un projet, et qui bénéficie d’une documentation claire et d’une communauté relativement active.

Comme l’indique Laurent, le Lua est multi-paradigme. S’il se présente au premier abord comme un langage impératif, on peut également faire de l’objet (au prix de l’utilisation d’une bibliothèque) et il dispose de plus de tous les outils pour faire du fonctionnel (il me semble juste que l’on ne peut pas faire simplement de curryfication). Du point de vue de la syntaxe, c’est un langage très agréable qui permet d’exprimer les choses de façon très succincte. Ainsi on peut itérer sur un tableau avec un « foreach », on dispose de fonctions locales, il n’y a pas de point-virgule de fin de ligne, pas de parenthèses superflues…

Par contre c’est un langage qui souffre d’un très gros laxisme du point de vue du typage. Il est en effet typé (dynamiquement) dans le sens où l’on peut demander le type d’une variable, mais dans de nombreux cas si l’on utilise une variable d’un type au lieu d’un autre, elle est transtypée sans que cela pose de problème au langage. De plus on ne peut pas imposer le type attendu par une fonction, mais juste le vérifier à l’exécution. Enfin si l’on tente d’accéder à une variable qui n’existe pas, par exemple suite à une faute de frappe, cela ne gène pas le moins du monde le langage, qui renverra « nil » (valeur valide pour une variable). Naturellement c’est source de beaucoup d’erreurs, décelées seulement à l’exécution de la branche erronée.

Autres choix qui ne me plaisent pas : le fait que les variables soient globales sauf contre-indication explicite, et que les opérateurs logiques « or » et « and » qui, contrairement à ce que l’on pourrait attendre d’un langage moderne, ne renvoient pas un booléen mais l’opérande déterminante.

Du côté des outils fournis, le langage se veut doté d’un système de table très puissant, et s’il est vrai qu’il est pratique dans les cas simples, on en trouve très vite les limites. Par exemple on ne dispose pas de reverse-iterator, et en écrire un s’avère laborieux. De même, la fonction permettant de compter les éléments d’une table est en fait une sorte de hack qui ne fonctionne que pour les tables indexées, en renvoyant le plus grand indice consécutif. Si l’on veut une fonction sérieuse pour connaître cette information, on doit donc l’écrire. Le langage se veut également doté d’une gestion de chaînes puissantes. Pourtant la documentation explique que les expressions rationnelles ne sont pas POSIX pour des raisons de quantité de code nécessaire. Au final filtrer des motifs devient vite pénible dès que l’on sort des cas d’école, et l’ajout d’une nouvelle syntaxe maison vient perturber le principe de moindre surprise.

Enfin du point de vue des performances, le langage se place apparemment plutôt bien pour un langage interprété. Il est notamment possible de faire de la compilation just-in-time et d’atteindre ainsi des performances très correctes. Cela dit elles restent plombées apparemment par le typage dynamique. Un simple appel de fonction se révèle être déjà coûteux. Du point de vue de la gestion de la mémoire, il est assez difficile de savoir ce que l’interpréteur fait, mais il semblerait qu’il ne sache pas tirer parti du fait qu’une fonction est souvent appelée pour optimiser l’allocation de ses variables locales. À vérifier cela dit.

En conclusion c’est un langage très pratique, vivant, facilement intégrable, avec une syntaxe relativement agréable, mais qui souffre d’un trop grand manque de rigueur.

6 réflexions sur « Une critique du Lua »

  1. Je ne vois pas en quoi Lua serait pire qu’un langage interprété quelconque. C’est du « timebomb typing », c’est vrai, mais en quoi serait-ce plus pourri que Perl ou PHP (je connais assez peu ce dernier langage mais je doute que ce soit franchement mieux).

    OK, les globales saimal. Mais pour un script très court (enchaînement de commandes simples ou itération sur une liste et traitement de base) c’est une bénédiction de n’avoir qu’une syntaxe légère et pas chiante. On n’en est pas à déclarer l’itérateur de sa collection comme en C++ : à part un développeur Java, qui veut écrire cinq lignes lourdes et inutiles pour décrire le concept du FOR sur un objet de la lib standard du langage ?

    Les regexp non POSIX, soit. Mais argumente davantage pour cela. A-t-on les wildcards de base, les ensembles, tout ça… bref, même pour des cas « non d’école », j’oserais presque dire qu’un fnmatch suffit souvent. Fournis des exemples de non-compatibilité flagrante et dangereuse, et on verra.

    Quant aux variables avec des typos dedans, je ne connais pas de compilateur qui te propose la correction lexicale à ce point. Tout au plus peux tu éviter le problème avec la complétion de ton IDE, ou remercier G++ de te proposer les candidats les plus proches quand tu te plantes dans le type des paramètres à une fonction.

    Bref, je ne dis pas que Lua tue, mais il me semble qu’il est intégré dans des tas de logiciels (ion3 et WoW pour ne citer qu’eux) et qu’il n’est pas foncièrement pire qu’un autre. Le but est d’être un langage de script léger (et à mon avis c’est un succès), pas de proposer le compilateur du futur avec du typage polaire et une correction d’erreur télépathique.

  2. Je ne dis pas que Lua est pire qu’un autre langage de script (dire qu’il est pire que le PHP serait de la mauvaise foi éhontée), je souligne juste ce que je considère comme des défauts.
    Concernant les globales, en avoir est effectivement très pratique, mais lorsque l’on écrit le code suivant, on pourrait préférer que par défaut, foo soit une variable locale dont la portée est limitée à la fonction. Ce n’est pas le cas, comme le montre le print.

    local function foobar()
    foo = 4
    print(« foo »)
    end
    foobar()
    print( foo )

    Je ne dis pas non plus qu’il devrait faire de la correction d’erreur, juste qu’il devrait être moins permissif. Par exemple le code suivant fonctionnera sans broncher, alors qu’une erreur serait la bienvenue :

    toto = 42
    tata = toot

    Pour les expressions rationnelles, la compatibilité est de toute façon cassée ne serait-ce que parce que le caractère d’échappement est « % » lorsque partout ailleurs on utilise « \ ». Mais si par exemple je veux filtrer une chaîne contenant n occurences d’un motif, je ne peux pas. Pour sa défense, il y a aussi de bonnes choses : le « – » est intéressant par exemple.
    Pour plus de détails, je te renvoie au chapitre du livre : http://www.lua.org/pil/20.1.html

  3. Sly dit : Je ne vois pas en quoi Lua serait pire qu’un langage interprété quelconque.

    Hum… Parce que ça n’a pas le même but ? A priori, Lua est fait pour être embarqué et sans overhead, pas pour scripter au quotidien.

  4. Au sujet du or et du and, c’est quelque chose d’assez courant dans les langages dynamiques.

    Ça permet, par exemple, de simplifier l’écriture de ce type de code (fréquent) :

    tmp = getBidule()
    return tmp != null ? tmp : « Nothing »

    en celui-ci :

    return getBidule() or « Nothing »

    Ce dernier exprime tout de même d’une façon plus immédiate à lire le sens « ou sinon » qu’on cherche à exprimer. Après, idéalement, pour moi, la solution est d’utiliser un opérateur séparé différent du « ou » logique.

  5. Je comprends bien l’idée, mais je ne trouve pas que travestir un opérateur booléen en truc de type indéterminé produise un résultat plus simple, bien au contraire.

    Donc comme toi, je pense que ce qu’il faut c’est un autre opérateur.

  6. Pour les globales par défaut, à mon avis c’est un choix qui doit simplifier l’implémentation. Et c’est recommandé de les utiliser d’après la doc (« It is good programming style to use local variables whenever possible. Local variables help you avoid cluttering the global environment with unnecessary names. Moreover, the access to local variables is faster than to global ones »):

    foo = 0
    local function foobar()
    local foo = 4
    print(« foo »)
    end
    foobar()
    print( foo )

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *