/* Structural comparison */
#define LESS -1
#define EQUAL 0
#define GREATER 1
#define UNORDERED (1L << (8 * sizeof(value) - 1))
/* The return value of compare_val is as follows:
> 0 v1 is greater than v2
0 v1 is equal to v2
< 0 and > UNORDERED v1 is less than v2
UNORDERED v1 and v2 cannot be compared */
static intnat compare_val(value v1, value v2, int total)
{
struct compare_item * sp;
tag_t t1, t2;
sp = compare_stack;
while (1) {
if (v1 == v2 && total) goto next_item;
if (Is_long(v1)) {
if (v1 == v2) goto next_item;
if (Is_long(v2))
return Long_val(v1) - Long_val(v2);
/* Subtraction above cannot overflow and cannot result in UNORDERED */
if ((Is_atom(v2) || Is_young(v2) || Is_in_heap(v2)) &&
Tag_val(v2) == Forward_tag) {
v2 = Forward_val(v2);
continue;
}
return LESS; /* v1 long < v2 block */
}
if (Is_long(v2)) {
if ((Is_atom(v1) || Is_young(v1) || Is_in_heap(v1)) &&
Tag_val(v1) == Forward_tag) {
v1 = Forward_val(v1);
continue;
}
return GREATER; /* v1 block > v2 long */
}
/* If one of the objects is outside the heap (but is not an atom),
use address comparison. Since both addresses are 2-aligned,
shift lsb off to avoid overflow in subtraction. */
if ((!Is_atom(v1) && !Is_young(v1) && !Is_in_heap(v1)) ||
(!Is_atom(v2) && !Is_young(v2) && !Is_in_heap(v2))) {
if (v1 == v2) goto next_item;
return (v1 >> 1) - (v2 >> 1);
/* Subtraction above cannot result in UNORDERED */
}
t1 = Tag_val(v1);
t2 = Tag_val(v2);
if (t1 == Forward_tag) { v1 = Forward_val (v1); continue; }
if (t2 == Forward_tag) { v2 = Forward_val (v2); continue; }
if (t1 != t2) return (intnat)t1 - (intnat)t2;
switch(t1) {
case String_tag: {
mlsize_t len1, len2, len;
unsigned char * p1, * p2;
if (v1 == v2) break;
len1 = caml_string_length(v1);
len2 = caml_string_length(v2);
for (len = (len1 <= len2 ? len1 : len2),
p1 = (unsigned char *) String_val(v1),
p2 = (unsigned char *) String_val(v2);
len > 0;
len--, p1++, p2++)
if (*p1 != *p2) return (intnat)*p1 - (intnat)*p2;
if (len1 != len2) return len1 - len2;
break;
}
case Double_tag: {
double d1 = Double_val(v1);
double d2 = Double_val(v2);
if (d1 < d2) return LESS;
if (d1 > d2) return GREATER;
if (d1 != d2) {
if (! total) return UNORDERED;
/* One or both of d1 and d2 is NaN. Order according to the
convention NaN = NaN and NaN < f for all other floats f. */
if (d1 == d1) return GREATER; /* d1 is not NaN, d2 is NaN */
if (d2 == d2) return LESS; /* d2 is not NaN, d1 is NaN */
/* d1 and d2 are both NaN, thus equal: continue comparison */
}
break;
}
case Double_array_tag: {
mlsize_t sz1 = Wosize_val(v1) / Double_wosize;
mlsize_t sz2 = Wosize_val(v2) / Double_wosize;
mlsize_t i;
if (sz1 != sz2) return sz1 - sz2;
for (i = 0; i < sz1; i++) {
double d1 = Double_field(v1, i);
double d2 = Double_field(v2, i);
if (d1 < d2) return LESS;
if (d1 > d2) return GREATER;
if (d1 != d2) {
if (! total) return UNORDERED;
/* See comment for Double_tag case */
if (d1 == d1) return GREATER;
if (d2 == d2) return LESS;
}
}
break;
}
case Abstract_tag:
compare_free_stack();
caml_invalid_argument("equal: abstract value");
case Closure_tag:
case Infix_tag:
compare_free_stack();
caml_invalid_argument("equal: functional value");
case Object_tag: {
intnat oid1 = Oid_val(v1);
intnat oid2 = Oid_val(v2);
if (oid1 != oid2) return oid1 - oid2;
break;
}
case Custom_tag: {
int res;
int (*compare)(value v1, value v2) = Custom_ops_val(v1)->compare;
if (compare == NULL) caml_invalid_argument("equal: abstract value");
caml_compare_unordered = 0;
res = Custom_ops_val(v1)->compare(v1, v2);
if (caml_compare_unordered && !total) return UNORDERED;
if (res != 0) return res;
break;
}
default: {
mlsize_t sz1 = Wosize_val(v1);
mlsize_t sz2 = Wosize_val(v2);
/* Compare sizes first for speed */
if (sz1 != sz2) return sz1 - sz2;
if (sz1 == 0) break;
/* Remember that we still have to compare fields 1 ... sz - 1 */
if (sz1 > 1) {
sp++;
if (sp >= compare_stack_limit) sp = compare_resize_stack(sp);
sp->v1 = &Field(v1, 1);
sp->v2 = &Field(v2, 1);
sp->count = sz1 - 1;
}
/* Continue comparison with first field */
v1 = Field(v1, 0);
v2 = Field(v2, 0);
continue;
}
}
next_item:
/* Pop one more item to compare, if any */
if (sp == compare_stack) return EQUAL; /* we're done */
v1 = *((sp->v1)++);
v2 = *((sp->v2)++);
if (--(sp->count) == 0) sp--;
}
}
Moumou :
Sally > Ben en fait je n'ai jamais fait d'OO à la caml, mais je pensais que ça marchait un peu pareil que java, non ? Ie quand on déclare une classe on déclare qu'elle hérite d'une autre, et dans ce cas toute fonction qui prend en argument la classe mère peut prendre la classe fille.
Moumou :
Ben alors c'est bien du sous-typage. Enfin c'est ce que moi et le futur chef de Sally appelons du sous-typage
Moumou :
Sally > Ben en fait je n'ai jamais fait d'OO à la caml, mais je pensais que ça marchait un peu pareil que java, non ? Ie quand on déclare une classe on déclare qu'elle hérite d'une autre, et dans ce cas toute fonction qui prend en argument la classe mère peut prendre la classe fille.
# class chaine = object(self) method longueur = 42 end;; class chaine : object method longueur : int end # class caractere = object(self) method longueur = 1 method nom_unicode = "HOT BEVERAGE" end;; class caractere : object method longueur : int method nom_unicode : string end # let paire_vers_liste x y = [x;y];; val paire_vers_liste : 'a -> 'a -> 'a list = <fun> # let nimportequoi_vers_chaine c = (c:>chaine);; val nimportequoi_vers_chaine : #chaine -> chaine = <fun> # let caractere_vers_chaine (c:caractere) = (c:>chaine);; val caractere_vers_chaine : caractere -> chaine = <fun> # let fonction (x:chaine) (y:caractere) = paire_vers_liste x y;; This expression has type caractere = < longueur : int; nom_unicode : string > but is here used with type chaine = < longueur : int > Only the first object type has a method nom_unicode
Pollux :
Et bien sûr que si il y a du downcasting possible sans Obj.magic, heureusement (c'est l'upcasting qui n'est pas possible)
Le problème, c'est pas qu'il n'est pas possible, c'est plutôt que l'inférence de type n'est pas toujours capable de le faire tout seul et qu'il faut l'"aider" avec :>...
❰13:44❱ benjamin@bookeldor-main:~/merdier❯ocaml Objective Caml version 3.08.3 # class mother = object(o) method m () = print_string "mother\n" end;; class mother : object method m : unit -> unit end # class daughter = object(o) inherit mother as mom method m2 () = print_string "daughter\n" end;; class daughter : object method m : unit -> unit method m2 : unit -> unit end # let plop () = let c = new mother and d = new daughter in let e = (d :> mother) in c#m (); d#m2 (); (e :> daughter)#m2 () ;; This expression cannot be coerced to type daughter = < m : unit -> unit; m2 : unit -> unit >; it has type mother but is here used with type #daughter Only the second object type has a method m2 # let plop_qui_marche () = let c = new mother and d = new daughter in let e = (d :> mother) in c#m (); d#m2 (); ((Obj.magic e) : daughter)#m2 () ;; val plop_qui_marche : unit -> unit = <fun> # plop_qui_marche ();; mother daughter daughter - : unit = ()
match (instanceof o) with mother -> o#m | daughter -> o#m2 ;;
BookeldOr :
oui c'est sympa, et d'ailleurs Obj.magic n'est pas là pour rien
BookeldOr
: un truc sympa ça serait de faire un <upcast>
Pollux :
Ben non, comme je l'ai dit à mon avis c'est "by design" que c'est comme ça...
BookeldOr
:Pollux :de toutes façons, comme j'ai dit, actuellement ça n'est pas possible facilement
Ben non, comme je l'ai dit à mon avis c'est "by design" que c'est comme ça...
et puis, pour expliciter que la fonction traite les classes de manière défférente, on pourrait imaginer d'écrire son type en mettant en évidence la disjonction, par exemple
(mother | daughter) -> ()
BookeldOr :
au fait,
vous avez essayé de typer le prog que j'ai posté un peu plus haut ?
c'est assez marrant
Pollux
:BookeldOr
:Pollux :de toutes façons, comme j'ai dit, actuellement ça n'est pas possible facilement
Ben non, comme je l'ai dit à mon avis c'est "by design" que c'est comme ça...
Oui, et "comme j'ai dit" c'est volontaire que ça ne soit pas possible ^^et puis, pour expliciter que la fonction traite les classes de manière défférente, on pourrait imaginer d'écrire son type en mettant en évidence la disjonction, par exemple
(mother | daughter) -> ()
C'est vrai que ça pourrait être sympa d'avoir un type "somme croissante", de la forme A | B | C avec A<B<C, qui pourrait être considéré de façon transparente comme un #A dans tous les cas, puis par pattern-matching pourrait être séparé en #B / A (ou en #C / B)
Parce que là les alternatives c'est :
- représenter ça comme une somme disjointe normale, mais c'est un peu pénible pour utiliser le plus grand sous-type commun (ce qui risque d'être pourtant l'utilisation la plus fréquente)
- représenter ça comme le plus grand sous-type commun, avec un "daughter option" en plus pour les opérations spécifiques aux daughter (mais c'est un peu crade, par exemple ça ne fournit aucune garantie que le daughter serait, s'il est présent, physiquement égal au mother)
BookeldOr :
au fait,
vous avez essayé de typer le prog que j'ai posté un peu plus haut ?
c'est assez marrant
./75 ?
let p x y = fun z -> z x y ;; let q () = let x1 = fun x -> p x x in let x2 = fun z -> x1 (x1 z) in let x3 = fun z -> x2 (x2 z) in let x4 = fun z -> x3 (x3 z) in let x5 = fun z -> x4 ( x4 z) in x5 (fun z -> z);;
BookeldOr :
pour vérifier ça, un daughter ref option, et on peut vérifier si c'est bien le meme paramètre que le premier avec l'opé == mais c'est encore plus lourd
let p x y = fun z -> z x y ;; let q () = let x1 = fun x -> p x x in let x2 = fun z -> x1 (x1 z) in let x3 = fun z -> x2 (x2 z) in let x4 = fun z -> x3 (x3 z) in let x5 = fun z -> x4 ( x4 z) in x5 (fun z -> z);;
(facile à comprendre que le type généré croit très vite, avec un x6 c'est encore plus long, etc...
Moumou :Non ça n'a absolument aucun rapport avec java. Une fois qu'un objet est défini on oublie complètement avec quelle classe on l'a défini, une classe peut hériter de plusieurs classes différentes, et si tu veux que deux objets de deux classes différentes qui ont les mêmes méthodes soient considérés comme de types incompatibles (comme en java) il faut masquer les types concrets dans la signature du module (mais dans ce cas autant ne pas utiliser des objets...)
Sally > Ben en fait je n'ai jamais fait d'OO à la caml, mais je pensais que ça marchait un peu pareil que java, non ? Ie quand on déclare une classe on déclare qu'elle hérite d'une autre, et dans ce cas toute fonction qui prend en argument la classe mère peut prendre la classe fille.
> oui pour que ça passe il faut aller jusqu'à # let fonction = (paire_vers_liste : chaine -> chaine -> chaine list :> chaine -> caractere -> chaine list);; val fonctisinon il n'arrive pas à voir que la coercition est correcte