Date: Thu, 08 Oct 1998 14:42:01 +0100
From: Serge Fantino <fantino@math.unice.fr>
To: Jerome Vouillon <Jerome.Vouillon@inria.fr>
Subject: Re: porte des definitions / initialisation des variables de classe
Jerome Vouillon wrote:
> On Wed, Oct 07, 1998 at 02:03:21PM +0100, Serge Fantino wrote:
> > Bon. Je vais essayer de vous faire sentir ce qui me gene sur un exemple:
> >
> > class test1 =
> > object
> > val x = Random.int 1000
> > method get = x
> > end
> >
> > Je m'attends a ce que chaque nouvel objet cree a partir de test1 soit initialise
> > avec une valeur différente pour x.
> > En effet, l'initialisation est effectuee dans le corps de l'"object", elle ne
> > devrait donc etre effectuee que lorsque j'instancie un nouvel objet.
>
> Le contenu des enregistrements (dans le language de base) et des
> structures (dans le langage de module) est évalué. Il me paraît donc
> naturel que ce soit également le cas pour "object ... end" dans le
> langage de classe.
>
Ce qui est naturel, c'est que l'objet soit initialisé lorsqu'il est créé. Or l'objet
est créé lorsqu'on appelle "new", pas lorsque l'on définit la classe...
Que ce passe-t-il dans caml ? (j'espere que vous me corrigerez...)
- "class xxx" définit la classe "xxx", qui n'est pas une valeur caml standard (elle
vit dans l'espace des classes, avec un espace de nom séparé,...)
- "class xxx" définit également un constructeur, qui est une fonction caml standard à
valeur "object"
- "new" est une fonction spéciale, définie de l'espace des classes dans l'espace des
valeurs standards: elle associe à un nom de classe son constructeur, permettant
d'instancier un nouvel objet
Le fonctionnement normal d'un objet est donc le suivant:
* les objets sont initialisés à chaque appel à "new"
* les valeurs des variables d'instances sont a priori indépendantes (sauf si
explicitement lors de l'instanciation JE décide de partager une valeur entre certains
objets, OU si je définis le constructeur de classe de tel sorte qu'il définisse un
environnement commun à tous les objets)
Maintenant, examinons le cas où la classe est définie "sans paramètre":
- le constructeur n'est pas une fonction à valeur "object" mais simplement une
constante "object": l'objet est instancié lors de la définition de la classe
- la fonction spéciale "new" retourne une copie de l'objet constant, ce qui a comme
conséquences:
* tous les objets de la classe sont initialisés avec les memes valeurs
* toutes les valeurs "pointeurs" (array,hashtbl,ref,...) sont partagées par tous
les objets de la classe (par contre les variables déclarées "mutable" sont
indépendantes (?))
Est-ce que c'est correct ?
Si oui, il me semble alors que la sémantique du langage est uniquement conditionnée
par l'implémentation.
Je ne vois aucune raison a priori pour que le mécanisme d'instanciation soit
différent selon que la classe accepte ou non des paramètres. De plus je trouve
anormal que la définition d'une classe puisse induire l'instanciation d'un objet:
classes et objets "vivent" dans des espaces différents; seule la fonction spéciale
"new" devrait permettre de passer de l'un à l'autre.
Pour moi, l'implémentation de caml devrait vérifier:
"les variables d'instances sont initialisées à chaque création d'un nouvel objet".
Cela dit, je peux complètement me tromper, ne pas voir l'évidence, etre le seul à
trouver le fonctionnement actuel "counter-intuitive".
Cordialement,
Serge Fantino
PS: En OCaml-1.0x, la définition d'une classe sans paramètre était prohibée, ainsi
que l'évaluation partielle des constructeurs: la raison de ce bridage (volontaire ?)
n'était-elle pas d'éviter ce genre de problème ?
> > Actuellement, que ce passe-t-il ? Si je declare la classe test3:
> >
> > class test3 range =
> > object
> > val x = Random.int range
> > method get = x
> > end
> >
> > alors chaque nouvelle instance est initialisée avec une valeur
> > différente de \ x, ie l'initialisation est effectuée au moment de
> > l'instanciation.
> >
> > Si je declare maintenant:
> > class test3_1000 = test3 1000;;
> >
> > il est logique de s'attendre a ce que test3_1000 soit une classe
> > specialisée de test3, fonctionnant de facon similaire a test3. Les
> > expressions suivantes devraient retourner des resultats similaires,
> > ie des valeurs différentes <1000 pour chaque instance:
> > let l1 = List.map (fun _ -> (new test3 1000)#get) [0;0;0;0]
> > let l2 = List.map (fun _ -> (new test3_1000)#get) [0;0;0;0]
> >
> > Helas, la liste l2 repete la meme valeur... pourquoi ?
>
> Je ne comprends pas trop pourquoi ce résultat te choque. Les arguments
> que tu donnes pourraient tout aussi bien s'appliquer à l'exemple
> suivant :
>
> type t = { x : int }
> let test3 range = { x = Random.int range }
> let test3_1000 = test3 1000
> let l1 = List.map (fun _ -> (test3 1000).x) [0;0;0;0]
> let l2 = List.map (fun _ -> (test3_1000).x) [0;0;0;0]
>
> En fait, je crois que ce qui apparaît comme pas très intuitif est
> que deux objets puissent partager une même valeur mutable. Mais c'est
> également le cas lorsque l'on utilise Oo.copy ou {< ... >}.
>
> -- Jérôme
This archive was generated by hypermail 2b29 : Sun Jan 02 2000 - 11:58:16 MET