J'ai lu un peu en diagonale la discussion sur les sauts, mais voilà mon grain de sel : l'intérêt pratique d'une machine virtuelle, c'est d'avoir une représentation intermédiaire raisonnable entre un langage de haut niveau (mettons zlang) et le langage machine (ou les langages machines, d'où l'argument un peu fumeux de la portabilité de java). Il faut donc que ce soit suffisamment haut niveau pour que ce soit une cible de compilation agréable, mais suffisamment proche de ce qui se fait dans le silicium pour proposer des performances qu'on espère largement meilleures que celles d'une interprétation immédiate du programme initial.
Partant de là, un système de types très strict n'est pas indispensable (et je dis ça alors que je considère le contraire pour un langage de programmation).
Première digression : le principal intérêt d'un typage fort est que si le langage intermédiaire (ou la VM) est bien intégré à la compilation d'un langage de haut niveau, le typage du langage intermédiaire apporte un confort d'implémentation dudit compilateur : « ma passe de transformation est bien typée, donc j'ai certaines garanties sur sa correction ».
Ça a par contre un inconvénient certain, qui n'est pas tellement pertinent pour un langage de haut niveau : si je veux cibler le langage de Pulp avec un compilateur que j'écris pour un certain langage haut niveau, et que le langage de Pulp est fortement typé (comprendre « avec un système de types expressifs », pas « comme Python »), je dois non seulement générer du code correct, mais en plus je dois moralement convaincre le typeur de Pulp que ce code est correct. La deuxième partie peut poser des problèmes : il peut arriver qu'un système de types très expressif dans mon langage d'entrée m'assure que le code généré est le bon, mais que le typeur de Pulp ne soit pas prévu pour accepter ce genre de construction. C'est par exemple le cas de Coq : la génération vers OCaml utilise plein de Obj.magic : 'a -> 'b
pour forcer le typeur d'OCaml à accepter des programmes qui sont prouvés corrects mais qu'il n'est pas assez puissant pour valider.
Ceci étant dit, j'ai certes lu un peu rapidement, mais je ne pense pas que ce soit le genre de problèmes que vous puissiez rencontrer. J'ai l'impression que le typage que vous envisagiez est assez rudimentaire, et c'était donc plus parce que je pense que c'est une remarque intéressante (il y a un article pas très vieux sur linuxfr où bluestorm parle de ça, pour ceux qui l'ont connu, je peux le retrouver si ça vous amuse - sinon vous pouvez probablement chercher « Malfunction »).
Par contre, le problème des performances se pose réellement. Qu'on se mette bien d'accord : the first rule of optimisation is "don't", et la course aux micro-optimisations et aux benchmarks ridicules à la « if
ça prend 1ms de moins que switch
» n'est ni intéressante, ni productive. Pour autant, je pense que dans le cadre de l'implémentation d'une VM, c'est un point qui mérite d'être pris en compte pour la conception des instructions (parce que c'est justement l'intérêt de la chose).
Je pense précisément aux différences entre entiers et booléens. Dans un langage haut-niveau, mon avis est qu'il doit s'agir de deux types différents et incompatibles, parce qu'ils remplissent des rôles qui n'ont rien à voir. Il faut donc une fonction de conversion du genre is_not_zero
pour utiliser un entier tel quel dans un if
. De l'autre côté, votre processeur ne fait pas cette différence : tout est entier, et les booléens ne sont que des entiers comme les autres avec des opérations qui ont une sémantique qui fait qu'on a l'impression que c'est vrai ou faux.
Pour une VM, je pense que vous n'avez pas besoin de faire cette différence : c'est à l'implémentation du langage source de s'assurer qu'il ne fait pas n'importe quoi en générant du code. C'est un choix parfaitement raisonnable d'oublier complètement les booléens et d'avoir des instructions de saut qui manipulent des entiers, parce qu'on peut considérer que les booléens du langage source sont eux-mêmes compilés vers des entiers une fois qu'on a vérifié qu'ils étaient utilisés correctement, et que c'est à la charge du compilateur de faire attention à ne pas mélanger n'importe comment ces entiers là avec les autres.
Concrètement, ça veut dire que la fonction int_to_bool
du langage source est en fait, sous le capot, une simple indication au typeur pour le convaincre que le programme est correct, qui est traduite par l'identité une fois le programme compilé. Ainsi, le langage source profite d'un typage fort (et d'une implémentation micro-légèrement plus lente parce qu'il faut prendre le temps de ne pas traduire cette fonction), et la VM profite d'une implémentation plus efficace (pas d'appel de fonction inutile, et comme de toute façon son langage n'est pas à destination du programmeur, on n'a pas besoin de lui fournir ce genre de garanties).
Voilà pour ma contribution à ce débat. Comme je l'ai dit, j'ai lu un peu vite ce que vous avez écrit, et il est très possible que je dise ici des choses dont vous avez déjà parlé. Mon but ici n'est pas de vous dire « faites comme ça, c'est le meilleur choix » (je ne connais pas assez ce domaine pour en être sûr), mais pour vous inviter à réfléchir un peu en détail sur ce point avant de faire un choix par défaut. J'ai l'impression que certaines idées ou propositions de spécifications font comme s'il s'agissait d'un langage à destination du programmeur, et je pense que c'est un piège.
PS : je ne sais pas si c'est très clair dans mon message, mais mon avis n'est certainement pas de dire « à mort les types, tout est entier ». C'est totalement pertinent d'avoir un minimum de types différents dans le langage intermédiaire, qui dépendront probablement du langage source auquel vous voulez faciliter la vie (par exemple, des closures pour le fonctionnel, ou des objets). Il faut simplement faire attention à ne pas aller trop loin : tout ce qui est facilement gérable par un compilateur en amont peut être oublié.
PS2 : désolé pour le pavé. TL;DR : un système de types très expressif, c'est super pour un langage de programmation, mais c'est moins indispensable pour un langage cible et ça a un gros risque de compliquer le travail du compilateur.