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.
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
.
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
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 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.