Back

PIF - Changement

2012-04-23

Je suis en train de réfléchir à PIF, aux variables, aux types, aux références, et j’ai décidé de tout changer.

En fait, on ne fait que changer quelques mot-clefs. J’ai renommé let en var et const en let, ce que fait que l’on aura donc maintenant deux mot-clefs super proches, dont j’espère que la dénomination est suffisamment explicite, et dont il sera impératif de comprendre la distinction et le fonctionnement sous-jacent.

J’oublie ici tout ce qui était avant pour expliquer ce que je propose maintenant.

Le let

Lorsque l’on utilise un let, on crée juste un nouveau nom pour la valeur que l’on a donné. On n’a pas de référence sur cette valeur, ce qui signifie que l’on ne peut pas la changer.

let a = 42   # a est du type int, c'est à dire une valeur entière
a = 43       # invalide, ne compilera pas

C’est un peu comme ce que l’on fait en caml ou dans d’autres langages fonctionnels.

Remarquez en passant que les arguments d’une fonction sont immuables, c’est à dire que c’est comme s’ils avaient été déclarés avec un let.

Le var

Lorsque l’on utilise un var, on crée un nouvel espace mémoire que l’on remplit alors avec la valeur proposée, puis on attribue au nom donné la valeur d’une référence vers cet emplacement mémoire.

var a = 42  # a est du type &int, c'est à dire une référence sur un entier
a = 43      # ok ça marche

Le rapport avec la POO

Définissons le type suivant :

type Complex [
    re, im : float
]

Le problème vient des deux syntaxes suivantes, qui sont identiques :

var a : Complex = [12, 23]
var a = [12, 23] : Complex

Dans cette syntaxe lors de l’initialisation de l’objet avec les crochets, on crée un emplacement mémoire pour les données de l’objet de type Complex, obtenant ainsi une référence, c’est à dire un &Complex. On donne alors des valeurs aux variables de cet objet, puis (s’il y en a un), on appelle le constructeur sur cet objet, cela étant permis par le fait que l’on a une référence. Puis on déréférence ensuite l’objet pour obtenir une valeur de type Complex, qui est ensuite copiée dans un emplacement mémoire créée par le var pour obtenir une nouvelle référence. Que de complications.

Peut-être que ceci serait optimisé par LLVM, c’est d’ailleurs probable, celui-ci étant plutôt bien foutu. Mais fondamentalement, ça reste vraiment foireux. Mais heureusement, on peut aussi faire ça :

let a : &Complex = [12, 23]
# ou
let a = [12, 23] : &Complex

On crée ici une référence sur un Complex, qui n’est pas déréférencée mais bien attribuée telle quelle au nom a.

Lorsque l’on veut définir des objets que l’on souhaite bidouiller par la suite, il est donc impératif d’utiliser la deuxième syntaxe, avec un let et en spécifiant explicitement que l’on veut une référence, et pas la première.

Peut-être même plus

Peut-être faudrait-il, pour que cela soit encore plus parlant, introduire un constructeur de copie pour les types, c’est à dire une méthode qui serait appelée à chaque fois que l’on copie un objet dans un nouvel emplacement mémoire, que ce soit lors d’un var, lors du passage d’un objet comme paramètre, ou lors d’une affectation quelconque. On verrait alors bien que avec la première syntaxe présentée ci-dessus utilisant un var, on fait le cheminement suivant : construire A, copier A vers B, détruire A, alors que la syntaxe meilleure avec un let ne fait que construire A et le garder.