1

Suivant l'exemple du grand Hippopotamu, je vais remonter un troll polluxien qu'il a sauvagement abandonné en cours de route :
Sally :
Bon je vais résumer la situation en quelques points pour pas qu'on se perde smile

1/ Pollux : la surcharge n'existe pas en caml.
Moi : les appels de méthodes pour les objets fonctionnent exactement comme ce que j'ai compris qu'est la surcharge.
Pollux : ok je ne savais pas.
Point clos.

2/ Pollux : le type exn est le seul type incomplet.
Moi : non il y a aussi les types avec constructeurs variants.
Pollux : mais ça n'existait pas dans les vieilles versions.
Point clos.


3/ Pollux : mais moi je voudrais faire de la surcharge avec des structures plus simples que des objets.
Hippo et moi : c'est impossible, si tu tiens à le simuler tu peux utiliser des bidouilles avec les types unions mais bof.
Moi : mais à quoi ça va te servir concrètement cette surcharge ? je n'en comprends pas l'utilité...
La balle à Pollux.

4/ Pollux : mais pourtant les fonctions de comparaison utilisent une forme de surcharge.
Hippo : non ça utilise un type polymorphe.
Pollux : oui mais c'est implémenté en utilisant une surcharge.
Moi : où ça ? (pas dmc) il n'y a qu'une seule fonction compare... (peut-être ai-je mal compris ce qu'était la surcharge ?)
Pollux : réponse à côté de la plaque. Same player, shoot again.

5/ Pollux : je veux faire une fonction polymorphe de type 'a -> 'a -> bool qui trie les chaînes de manière case-insensitive et coïncide avec la fonction compare standard partout ailleurs.
Moi : c'est probablement impossible à réaliser exactement tel que tu le présentes (à moins d'écrire la fonction en C). Ceci dit si tu m'expliques *pourquoi* tu veux réaliser une telle fonction je peux très probablement te proposer une solution un peu différente mais qui conviendra tout aussi bien (en effet je ne vois pas a priori d'exemple où ta fonction serait nécessaire...) Pollux ?

avatar
« Le bonheur, c'est une carte de bibliothèque ! » — The gostak distims the doshes.
Membrane fondatrice de la confrérie des artistes flous.
L'univers est-il un dodécaèdre de Poincaré ?
(``·\ powaaaaaaaaa ! #love#

2

Pollux
:
Sally :
Bon je vais résumer la situation en quelques points pour pas qu'on se perde smile

1/ Pollux : la surcharge n'existe pas en caml.
Moi : les appels de méthodes pour les objets fonctionnent exactement comme ce que j'ai compris qu'est la surcharge.
Pollux : ok je ne savais pas.
Point clos.

2/ Pollux : le type exn est le seul type incomplet.
Moi : non il y a aussi les types avec constructeurs variants.
Pollux : mais ça n'existait pas dans les vieilles versions.
Point clos.

Je parlais de CAML Light, et ces deux points n'existent qu'en OCaml...
3/ Pollux : mais moi je voudrais faire de la surcharge avec des structures plus simples que des objets.
Hippo et moi : c'est impossible, si tu tiens à le simuler tu peux utiliser des bidouilles avec les types unions mais bof.
Moi : mais à quoi ça va te servir concrètement cette surcharge ? je n'en comprends pas l'utilité... La balle à Pollux.

Alors là, tu déformes ma question initiale. Je dis que la surcharge d'OCaml est peut-être très bien, mais Hippo me soutenait qu'en Caml Light on peut presque simuler une surcharge avec des unions : je lui dis que c'est loin d'être le cas...
4/ Pollux : mais pourtant les fonctions de comparaison utilisent une forme de surcharge.
Hippo : non ça utilise un type polymorphe.
Pollux : oui mais c'est implémenté en utilisant une surcharge.
Moi : où ça ? (pas dmc) il n'y a qu'une seule fonction compare... (peut-être ai-je mal compris ce qu'était la surcharge ?) Pollux : réponse à côté de la plaque. Same player, shoot again.

Alors je répète : il n'existe pas de programme Caml Light qui puisse réimplémenter une telle fonction polymorphe sans passer par la forme builtin. (même si on se contente de ne gérer que quelques types) Pourquoi? Parce qu'il n'y a pas de type "(string -> string -> bool) ∩ ('a list -> 'a list -> bool)"...
(je répète, je ne parle pas d'OCaml !)
5/ Pollux : je veux faire une fonction polymorphe de type 'a -> 'a -> bool qui trie les chaînes de manière case-insensitive et coïncide avec la fonction compare standard partout ailleurs.
Moi : c'est probablement impossible à réaliser exactement tel que tu le présentes (à moins d'écrire la fonction en C). Ceci dit si tu m'expliques *pourquoi* tu veux réaliser une telle fonction je peux très probablement te proposer une solution un peu différente mais qui conviendra tout aussi bien (en effet je ne vois pas a priori d'exemple où ta fonction serait nécessaire...) Pollux ?

Effectivement, dans la mesure où le polymorphisme n'est jamais explicitement nécessaire (quitte à recopier la fonction polymorphe N fois), il y aura toujours un workaround possible. Mais par exemple, je ne peux pas choisir par défaut d'utiliser une comparaison case-insensitive par défaut pour mes appels à sort()... Et je ne trouve pas ça très satisfaisant de se dire que les fonctions builtin ont un statut très particulier qu'on ne peut pas recréer.

avatar
« Le bonheur, c'est une carte de bibliothèque ! » — The gostak distims the doshes.
Membrane fondatrice de la confrérie des artistes flous.
L'univers est-il un dodécaèdre de Poincaré ?
(``·\ powaaaaaaaaa ! #love#

3

Sallyyyyyyyyyyyyyy je t'aime !!!!!!! love
Les droits inaliénables du troll :
1) le droit d'avoir raison
2) le droit d'être péremptoire
3) le droit de ne pas lire
4) le droit de ne pas répondre
5) le droit d'être de mauvaise foi
6) Autant pour moi / Faignant / Vivent Tintin et Milou

4

Hippopotamu
:
Alors là, tu déformes ma question initiale. Je dis que la surcharge d'OCaml est peut-être très bien, mais Hippo me soutenait qu'en Caml Light on peut presque simuler une surcharge avec des unions : je lui dis que c'est loin d'être le cas...

what
Je n'ai pas cessé de dire qu'il n'y a pas de surcharge en caml...
Pourquoi? Parce qu'il n'y a pas de type "(string -> string -> bool) ∩ ('a list -> 'a list -> bool)"...

Cette intersection est vide...

Tu veux sans doute parler d'un type union... Je ne comprends rien à ton histoire d'intersections...

Si ta fonction peut prendre des string et des lists, alors elle est d'un type réunion (somme). Et ça existe, je l'ai déjà dit (type ... = A of str -> str -> bool | B of lst -> lst -> bool;wink

avatar
« Le bonheur, c'est une carte de bibliothèque ! » — The gostak distims the doshes.
Membrane fondatrice de la confrérie des artistes flous.
L'univers est-il un dodécaèdre de Poincaré ?
(``·\ powaaaaaaaaa ! #love#

5

Pollux
:
Hippohmu
:
Alors là, tu déformes ma question initiale. Je dis que la surcharge d'OCaml est peut-être très bien, mais Hippo me soutenait qu'en Caml Light on peut presque simuler une surcharge avec des unions : je lui dis que c'est loin d'être le cas...

what Je n'ai pas cessé de dire qu'il n'y a pas de surcharge en caml...

Tu dis qu'avec des unions on peut faire un truc aussi puissant (même s'il n'y a pas l'avantage de modularité qu'offre la surcharge : tous les sens de la fonction n'ont pas à être définis au même endroit). Je dis que c'est impossible, à moins d'abandonner le typage statique au profit d'un typage dynamique (i.e. accepter (string|'a list) -> (string|'a list) -> bool et lever une exception si les deux types ne correspondent pas...)
Pourquoi? Parce qu'il n'y a pas de type "(string -> string -> bool) ∩ ('a list -> 'a list -> bool)"...
Cette intersection est vide...

Non (enfin sauf si tu restreins l'ensemble des types à ceux gérés par CAML).
On est bien d'accord que l'UNION DISJOINTE du type A et du type B est un type C qui représente soit un objet de type A, soit un objet de type B.
De façon duale, on peut définir l'INTERSECTION du type A et du type B comme un type C qui représente à la fois un objet de type A et un objet de type B.
Bien sûr, il faut définir ce qui se passe en cas d'application d'un objet de type A->A' *et* de type B->B' à un objet de type D : selon que D est de type A ou B, le type de l'application de C à D est A' ou B'.

Mais Caml ne gère effectivement pas les types intersection, on est d'accord.
Tu veux sans doute parler d'un type union... Je ne comprends rien à ton histoire d'intersections...

Si ta fonction peut prendre des string et des lists, alors elle est d'un type réunion (somme). Et ça existe, je l'ai déjà dit (type ... = A of str -> str -> bool | B of lst -> lst -> bool;wink
Non, parce que ça voudrait que soit la fonction accepte des str, soit elle accepte des lst. En l'occurrence, elle accepte les deux.

avatar
« Le bonheur, c'est une carte de bibliothèque ! » — The gostak distims the doshes.
Membrane fondatrice de la confrérie des artistes flous.
L'univers est-il un dodécaèdre de Poincaré ?
(``·\ powaaaaaaaaa ! #love#

6

Hippopotamu
:
Pollux
: Tu dis qu'avec des unions on peut faire un truc aussi puissant

Ah bon, j'ai dis ça? Mais moi je m'en fous, des surcharges, j'aime pas, et je ne vois pas où est la puissance...
Non (enfin sauf si tu restreins l'ensemble des types à ceux gérés par CAML).
On est bien d'accord que l'UNION DISJOINTE du type A et du type B est un type C qui représente soit un objet de type A, soit un objet de type B.
De façon duale, on peut définir l'INTERSECTION du type A et du type B comme un type C qui représente à la fois un objet de type A et un objet de type B.
Bien sûr, il faut définir ce qui se passe en cas d'application d'un objet de type A->A' *et* de type B->B' à un objet de type D : selon que D est de type A ou B, le type de l'application de C à D est A' ou B'.
Mais Caml ne gère effectivement pas les types intersection, on est d'accord.

Ben si !!
L'intersection des types (int list * 'a) et ('a list * ('b -> 'c)) est le type (int list * ('a -> 'b)), caml sait le calculer.
Et l'intersection des types string et list est vide (donc le typeur signale une erreur) : aucun objet n'est à la fois de type string et de type list.

Tu fumes, là!
Non, parce que ça voudrait que soit la fonction accepte des str, soit elle accepte des lst. En l'occurrence, elle accepte les deux.

type ... = A of str -> str -> bool | B of lst -> lst -> bool;;

Ben elle accepte les deux confus

avatar
« Le bonheur, c'est une carte de bibliothèque ! » — The gostak distims the doshes.
Membrane fondatrice de la confrérie des artistes flous.
L'univers est-il un dodécaèdre de Poincaré ?
(``·\ powaaaaaaaaa ! #love#

7

Sally :
1-2 > oui oui, j'avais bien compris smile
3 > ok j'ai dû mal comprendre la question (y avait quand même plein de posts très gros hein ^^)
4 > mais euh ! la question est « où dans l'implémentation vois-tu qu'on ait utilisé une surcharge ? » ce n'est pas à ça que tu réponds ! j'en conclus que la réponse est « nulle part » et que cette surcharge tu l'avais rêvée. Ce n'est pas bien grave mais tu aurais pu l'admettre tout seul wink

Pour ta « réponse » : en fait ce n'est pas « même si » on se restreint à quelques types, c'est « surtout si » on se restreint à quelques types smile. Parce que 'a est un type polymorphe valide mais tu ne peux bien sûr pas le restreindre pour qu'il n'ait le droit de prendre que deux valeurs. Car en fait c'est bien ça que tu voudrais faire, pas un type « intersection » : tu voudrais une variable de type 'a qui n'ait le droit de prendre que les valeurs string et 'b list. Ça autant que je sache c'est impossible, du moins en caml light (je ne te parle plus d'objets hehe)
5 >
Effectivement, dans la mesure où le polymorphisme n'est jamais explicitement nécessaire (quitte à recopier la fonction polymorphe N fois), il y aura toujours un workaround possible.

dans ton exemple N = 2 : les strings tu utilises ta fonction personnalisée, le reste tu utilises la fonction compare normale.
Ça te fait donc en tout *une* fonction à écrire, mais de toute façon tu l'aurais écrite, même si tu avais un quelconque moyen de l'« intégrer » dans l'autre. Et si tu avais ce moyen, tu distinguerais les types à l'intérieur de la fonction, là il faut les distinguer à l'extérieur, bref ça ne me paraît pas poser vraiment de problème tout ça.
Mais par exemple, je ne peux pas choisir par défaut d'utiliser une comparaison case-insensitive par défaut pour mes appels à sort()

C'est du caml-light encore ça ? parce que dans ocaml List.sort et Array.sort prennent en paramètre des fonctions de comparaison, il n'y a pas de fonction par défaut... (donc si tu veux utiliser my_compare plutôt que compare ben tu fais List.sort my_compare liste au lieu de List.sort compare liste, c'est pas tellement plus compliqué cheeky)
Et je ne trouve pas ça très satisfaisant de se dire que les fonctions builtin ont un statut très particulier qu'on ne peut pas recréer.

Euuuuuh c'est normal que les builtins aient un statut particulier quand même (d'ailleurs ce statut particulier est le statut "externe" c'est à dire que c'est une fonction importée d'un fichier objet ou une bibliothèque C. Tu peux bien sûr en faire d'autres toi-même...) Ce ne serait pas non plus évident de recréer l'addition si elle n'était pas déjà implémentée, il y a toujours des limites au bootstrap non ? neutral

avatar
« Le bonheur, c'est une carte de bibliothèque ! » — The gostak distims the doshes.
Membrane fondatrice de la confrérie des artistes flous.
L'univers est-il un dodécaèdre de Poincaré ?
(``·\ powaaaaaaaaa ! #love#

8

Ha oui je l'avais oublié celui-là happy
Justement j'avais commencé à réfléchir y a qqs jours sur un moyen d'améliorer le mécanisme de filtrage en Caml smile

Bon faut que je me replonge dans la discussion maintenant couic

« The biggest civil liberty of all is not to be killed by a terrorist. » (Geoff Hoon, ministre des transports anglais)

9

Bon en fait j'ai la flemme de lire tout ça en détail (surtout que cette histoire de types intersection vs union est très casse-gueule, selon le côté duquel on le regarde et selon la façon dont on définit l'application d'une fonction à une valeur ça peut vouloir dire tout et son contraire...)

Grosso modo, les trois critiques que j'avais envie de faire :
[ul][li]pas de surcharge : supposons que j'ai un operator+ : int -> int -> int, et que je veux définir une lib de calcul flottant, sans avoir une syntaxe complètement incongrue : j'aimerais bien pouvoir redéfinir cette opérateur pour pouvoir considérer aussi operator+ comme myfloat -> myfloat -> myfloat (pour éviter les pbs intersection/union : suivant le contexte, operator+ pourra prendre l'une de ces deux valeurs). Accessoirement, j'ai envie que cette surcharge se fasse sans que j'ai connaissance des surcharges actuellement définies par operator+ : je veux pouvoir utiliser deux libs qui font ce type de conversion simultanément.

[li]pas de conversion automatique : ma lib de calcul flottant aimerait bien pouvoir convertir automatiquement des entiers en flottants (puisqu'il y a injection, et que sémantiquement il n'y a pas vraiment de différence), i.e. je voudrais bien pouvoir faire surcharger une fonction "conversion : float -> int" qui serait appelée automatiquement par le compilo si elle peut aider à transformer une expression avec un type incorrect en expression avec un type correct.

Notez qu'on peut implémenter la surcharge à partir de la conversion automatique : si j'ai une fonction surchargée en int->int->int et float->float->float, je peux dire qu'elle de type T_Overload(int->int->int,float->float->float), et que j'ai une conversion automatique T_Overload(int->int->int,float->float->float) -> int -> int -> int et une conversion automatique T_Overload(int->int->int,float->float->float) -> float -> float -> float happy

Evidemment, ça doit poser des problèmes de typage pas forcément triviaux.

[li]filtrage très bas niveau : j'ai un gros code qui manipule, disons, des instructions ASM, que j'ai stockées sous la forme d'une liste. Soit la fonction :
let blah tmp = fun
| [] -> format tmp
| (Label l :: r) -> do_stuff (blah tmp r) l
| (instr :: r) -> blah (tmp+length instr) r

Si, une fois que j'ai écrite 30 fonctions du style, j'ai envie de modifier ma structure de données pour utiliser une liste doublement chaînée (parce que, par exemple, une optimisation a besoin de supprimer une instruction à un endroit quelconque dans le code), je l'ai vraiment DMC (je serais obligé d'écrire :
let blah tmp lst =
  if dl_list_empty lst then
    format tmp
  else
    match dl_hd lst with
    | Label l -> do_stuff (blah tmp (dl_tl lst)) l
    | instr -> blah (tmp+length instr) (dl_tl lst)

ce qui vous en conviendrez est bien plus compliqué et, surtout, très différent : une légère modification de structure de donnée nécessite de modifier tout le code : on se croirait en train de programmer en asm couic)

Le problème, c'est que le matching se fait uniquement sur la structure (donc c'est très bas niveau), alors que l'idéal serait de matcher sur des prédicats. On peut imaginer qu'on peut définir des fonctions "reconnaisseur" qui sont l'équivalent des constructeurs, mais en sens inverse. Par exemple on pourrait écrire l'exemple initial de cette manière :
let blah tmp = fun
| Iterator.empty -> format tmp
| (Iterator.hdtl (Label l) :: r) -> do_stuff (blah tmp r) l
| (Iterator.hdtl instr :: r) -> blah (tmp+length instr) r

Avec par exemple :
let Recognizer.Iterator.empty my_list =
  if list_empty my_list then
    ()
  else
    raise Unrecognized_Pattern

ou encore
let Recognizer.Iterator.hdtl my_list =
  try
    (hd my_list, tl my_list)
  with
    _ -> raise Unrecognized_Pattern


Avec la surcharge (trilove), on pourrait ainsi définir un filtrage bien plus puissant que l'actuel, et surtout bien plus propre...[/ul]

Bien entendu, je n'ai pas encore vraiment réfléchi à comment tout ça se typerait, donc ce n'est peut-être pas forcément réalisable tel quel (peut-être serait on obligé de faire une restriction à la surcharge pour n'accepter les surcharges que si le type du premier argument est différent : je pense que c'est implémenté dans OCaml [plusieurs méthodes peuvent avoir le même nom], mais est-ce qu'on peut aussi faire ça pour des opérateurs comme en C++ ?)
Mais si c'était possible de faire ces 3 choses, OCaml serait certainement largement plus utilisable, et même probablement agréable à utiliser (un filtrage flexible serait vraiment excellent top)

« The biggest civil liberty of all is not to be killed by a terrorist. » (Geoff Hoon, ministre des transports anglais)

10

1,2 > euh stdlib, module Num, ça fait ça...
qu'est-ce que tu reproches à (je simplifie) :
type num = Int of int | Float of float
let (+) a b = match a,b with
  Int x, Int y -> Int (x+y)
| Int x, Float y -> Float (x +. float_of_int y)
etc.
?

[EDIT] crétin de firefox neutral

3 > je réfléchis
avatar
« Le bonheur, c'est une carte de bibliothèque ! » — The gostak distims the doshes.
Membrane fondatrice de la confrérie des artistes flous.
L'univers est-il un dodécaèdre de Poincaré ?
(``·\ powaaaaaaaaa ! #love#

11

1,2 >

* est-ce qu'il y a conversion automatique int -> num ? hum
* le typage est bien trop grossier : si je fais (Int 2) + (Int 3), le typage m'affirme que le type résultant est "num". Ce qui a deux conséquences méchantes :
[ul]1] je vais avoir besoin de caster le résultat, et les casts, c'est MAL.
2] même si je n'ai pas nécessairement besoin de casts (par exemple je veux juste imprimer), je vais avoir une perte importante de performance lié au boxing des valeurs et au test dynamique sur le type (je voudrais un test statique)[/ul]

C'est-à-dire que finalement, ta solution correspond à :

int -> int -> num
int -> float -> num
float -> int -> num
float -> float -> num

et moi je voudrais

int -> int -> int
int -> float -> float
float -> int -> float
float -> float -> float

Accesoirement, ça ne résoud pas le pb des conversions implicites : je voudrais ne pas avoir à définir le cas int + float -> float (sinon, je vais vite avoir un code gigantesque, et puis ce n'est pas tjs possible : les développeurs de "int" et de "float" sont peut-être entièrement indépendants)

« The biggest civil liberty of all is not to be killed by a terrorist. » (Geoff Hoon, ministre des transports anglais)

12

3 > euh j'ai beau essayer j'arrive pas à comprendre ton exemple :-\ (enfin la partie sur les fonctions reconnaisseur, je comprends pas ce que tu voudrais que ça fasse)
([HS] euh let Recognizer.Iterator.hdtl = blabla ça marche pas hein, faut faire module Recognizer = struct module Iterator = struct let hdtl = blabla end end ^^[/HS])
avatar
« Le bonheur, c'est une carte de bibliothèque ! » — The gostak distims the doshes.
Membrane fondatrice de la confrérie des artistes flous.
L'univers est-il un dodécaèdre de Poincaré ?
(``·\ powaaaaaaaaa ! #love#

13

Beurk, mais quelle idée, de mélanger float et int couic
Voilà bien une manie que je n'ai jamais compris!
Ca n'a rien à voir, ça n'a pas la même signification, les opérations n'ont pas les mêmes propriétés algébriques.......
Les droits inaliénables du troll :
1) le droit d'avoir raison
2) le droit d'être péremptoire
3) le droit de ne pas lire
4) le droit de ne pas répondre
5) le droit d'être de mauvaise foi
6) Autant pour moi / Faignant / Vivent Tintin et Milou

14

Sally
: 3 > euh j'ai beau essayer j'arrive pas à comprendre ton exemple :-\ (enfin la partie sur les fonctions reconnaisseur, je comprends pas ce que tu voudrais que ça fasse)

Je veux que si je mets le pattern (MyPseudoConstructor x y), il appelle le reconnaisseur associé avec en paramètre la valeur à reconnaître, qui va soit :
* balancer une exception si la valeur n'est pas de cette forme-là (et alors le match..with va continuer au pattern suivant)
* renvoyer un couple dont les valeurs seront placés dans x et y

On peut ensuite raffiner, par exemple faire en sorte que le reconnaisseur ne soit pas une fonction statique mais un objet quelconque disposant d'une méthode recognize, ce qui permettrait de faire :

| ((IntGreaterThan 5) n) -> "l'entier "^n^" vaut plus de 5"
| _ -> "l'entier vaut moins de 5"

et plein d'autre trucs : ((EqualTo x)) par exemple ne reconnaîtrait que l'objet x.
([HS] euh let Recognizer.Iterator.hdtl = blabla ça marche pas hein, faut faire module Recognizer = struct module Iterator = struct let hdtl = blabla end end ^^[/HS])

Bien sûr, c'est du pipo, de toute façon le but ne serait pas forcément de faire des nouvelles classes dans Recognizer (on pourrait envisager un mot-clé spécial, par exemple).

« The biggest civil liberty of all is not to be killed by a terrorist. » (Geoff Hoon, ministre des transports anglais)

15

Hippopotamu :
Beurk, mais quelle idée, de mélanger float et int couic
Voilà bien une manie que je n'ai jamais compris! Ca n'a rien à voir, ça n'a pas la même signification, les opérations n'ont pas les mêmes propriétés algébriques.......

On parle juste de plonger là-dedans, oui ça n'a pas les mêmes propriétés et alors ? Du moment que c'est injectif, pas de problème... Et on ne prend pas l'utilisateur en traître : à un moment donné, il faudra bien qu'il fasse qqch avec le float, et s'il y a eu des conversions non désirées, il s'en rendra compte immédiatement.

« The biggest civil liberty of all is not to be killed by a terrorist. » (Geoff Hoon, ministre des transports anglais)

16

 | ((IntGreaterThan 5) n) -> "l'entier "^n^" vaut plus de 5"
| _ -> "l'entier vaut moins de 5" 


=>

| n when n>5 -> "l'entier "^(string_of_int n)^" vaut plus de 5"
| _ -> ...


Non???
Les droits inaliénables du troll :
1) le droit d'avoir raison
2) le droit d'être péremptoire
3) le droit de ne pas lire
4) le droit de ne pas répondre
5) le droit d'être de mauvaise foi
6) Autant pour moi / Faignant / Vivent Tintin et Milou

17

Pollux
: On parle juste de plonger là-dedans, oui ça n'a pas les mêmes propriétés et alors ? Du moment que c'est injectif, pas de problème...

Ben justement, ce n'est pas forcément injectif.
Et même pire : il n'y a aucune propriété de morphisme!
Et on ne prend pas l'utilisateur en traître : à un moment donné, il faudra bien qu'il fasse qqch avec le float, et s'il y a eu des conversions non désirées, il s'en rendra compte immédiatement.

Et après on se retrouve avec des purs problèmes de torture mentale quand on doit déterminer ce que fait vraiment le code...
Les droits inaliénables du troll :
1) le droit d'avoir raison
2) le droit d'être péremptoire
3) le droit de ne pas lire
4) le droit de ne pas répondre
5) le droit d'être de mauvaise foi
6) Autant pour moi / Faignant / Vivent Tintin et Milou

18

Pollux :
(euh au fait la lib num gère les rationnels, pas les flottants, mais le problème est le même)
* est-ce qu'il y a conversion automatique int -> num ? hum

évidemment non, mais ça servirait à quoi ? tu fais Int mon_int pis voilà (enfin tu peux peut-être faire en sorte que tout soit automatiquement des nums en définissant ta syntaxe dans ocamlp4, je sais pas j'ai jamais essayé d'utiliser ocamlp4)
* le typage est bien trop grossier : si je fais (Int 2) + (Int 3), le typage m'affirme que le type résultant est "num". Ce qui a deux conséquences méchantes : 1] je vais avoir besoin de caster le résultat, et les casts, c'est MAL.
Mais pourquoi tu veux caster ?? il suffit de n'utiliser toujours que des nums, si c'est bien fait c'est parfaitement transparent et tu n'as pas à savoir si dedans y a un float un int ou un je sais pas quoi... et comme ça si le type qui fait la lib num modifie des trucs ça ne change rien pour toi happy
2] même si je n'ai pas nécessairement besoin de casts (par exemple je veux juste imprimer), je vais avoir une perte importante de performance lié au boxing des valeurs et au test dynamique sur le type (je voudrais un test statique)

euh le test dynamique sur le type c'est un match tout bête, ça m'étonnerait un peu que ça influe sur les performances...
mais si tu veux un typage statique, ça veut dire que tu sais exactement au moment où tu écris le code ce qui va être des float et ce qui va être des int, non ? et si c'est le cas je ne comprends pas pourquoi tu te prends la tête à vouloir des conversions automatiques, tu n'en as pas besoin neutral
C'est-à-dire que finalement, ta solution correspond à :

int -> int -> num
int -> float -> num
float -> int -> num float -> float -> num

oui, enfin c'est plutôt num->num->num où num est soit un float soit un int en interne...
et moi je voudrais

int -> int -> int
int -> float -> float
float -> int -> float float -> float -> float

Mais si c'est juste ça que tu veux, tu peux utiliser des conversions explicites (comme ça, au moins, tu sais ce que tu fais... sinon tu aurais vite fait de te retrouver avec des float partout pour un petit oubli et de pas comprendre ce qui t'arrive couic)
Et puis ce que tu proposes passe encore pour l'addition, mais comment tu vas faire pour la division ? happy
avatar
« Le bonheur, c'est une carte de bibliothèque ! » — The gostak distims the doshes.
Membrane fondatrice de la confrérie des artistes flous.
L'univers est-il un dodécaèdre de Poincaré ?
(``·\ powaaaaaaaaa ! #love#

19

Hippopotamu :
 | ((IntGreaterThan 5) n) -> "l'entier "^n^" vaut plus de 5"
| _ -> "l'entier vaut moins de 5" 


=>

| n when n>5 -> "l'entier "^(string_of_int n)^" vaut plus de 5"
| _ -> ...

Non???

Absolument, mais l'idée est qu'après on peut faire des trucs non seulement plus flexibles mais avec une syntaxe plus simple (on pourrait envisager de simplifier "(IntGreaterThan 5) n" en "n>5"

(et oups pour le string_of_int, troll unintended wink)

« The biggest civil liberty of all is not to be killed by a terrorist. » (Geoff Hoon, ministre des transports anglais)

20

Hippopotamu
:
Pollux
: On parle juste de plonger là-dedans, oui ça n'a pas les mêmes propriétés et alors ? Du moment que c'est injectif, pas de problème...
Ben justement, ce n'est pas forcément injectif.

Si la taille est supérieure, si.
Et même pire : il n'y a aucune propriété de morphisme!

Et alors ?
Et on ne prend pas l'utilisateur en traître : à un moment donné, il faudra bien qu'il fasse qqch avec le float, et s'il y a eu des conversions non désirées, il s'en rendra compte immédiatement.
Et après on se retrouve avec des purs problèmes de torture mentale quand on doit déterminer ce que fait vraiment le code...

Non. On peut parfaitement faire en sorte dans l'IDE que si on laisse la souris sur le + ou si on fait F12 qd on est dessus, on voit le type effectivement utilisé.

« The biggest civil liberty of all is not to be killed by a terrorist. » (Geoff Hoon, ministre des transports anglais)

21

Pollux :
Je veux que si je mets le pattern (MyPseudoConstructor x y), il appelle le reconnaisseur associé avec en paramètre la valeur à reconnaître, qui va soit :
* balancer une exception si la valeur n'est pas de cette forme-là (et alors le match..with va continuer au pattern suivant) * renvoyer un couple dont les valeurs seront placés dans x et y


match reconnait machin with Some a, b -> ...
| None -> match autre_reconnait machin with...

ou alors :
try let (a, b) = reconnait machin in ...
with Unrecognized_pattern -> try let...

c'est ça ?
On peut ensuite raffiner, par exemple faire en sorte que le reconnaisseur ne soit pas une fonction statique mais un objet quelconque disposant d'une méthode recognize, ce qui permettrait de faire :

| ((IntGreaterThan 5) n) -> "l'entier "^n^" vaut plus de 5" | _ -> "l'entier vaut moins de 5"

hum oui ça c'est un bel exemple d'utilisation du when, comme l'a fait remarquer Hippo cheeky
avatar
« Le bonheur, c'est une carte de bibliothèque ! » — The gostak distims the doshes.
Membrane fondatrice de la confrérie des artistes flous.
L'univers est-il un dodécaèdre de Poincaré ?
(``·\ powaaaaaaaaa ! #love#

22

Non. On peut parfaitement faire en sorte dans l'IDE que si on laisse la souris sur le + ou si on fait F12 qd on est dessus, on voit le type effectivement utilisé.

Euh oui enfin personnellement ça me semble un chouïa inutilement compliqué neutral
avatar
« Le bonheur, c'est une carte de bibliothèque ! » — The gostak distims the doshes.
Membrane fondatrice de la confrérie des artistes flous.
L'univers est-il un dodécaèdre de Poincaré ?
(``·\ powaaaaaaaaa ! #love#

23

Sally
:
Pollux :
(euh au fait la lib num gère les rationnels, pas les flottants, mais le problème est le même)
* est-ce qu'il y a conversion automatique int -> num ? hum
évidemment non, mais ça servirait à quoi ? tu fais Int mon_int pis voilà (enfin tu peux peut-être faire en sorte que tout soit automatiquement des nums en définissant ta syntaxe dans ocamlp4, je sais pas j'ai jamais essayé d'utiliser ocamlp4)

La conversion automatique est bien plus puissante que la conversion *à la base* de tous les ints en nums... (pour des raisons d'efficacité, par exemple : une conversion peut représenter un surcoût [ici, c'est le boxing => x3 en mémoire, plus de pression sur le GC]; ou encore pour des raisons de typage : par exemple ici le type num est un type trop vague, et c'est justement tout l'intérêt des conversions automatiques de faire des plongements de ce type)
* le typage est bien trop grossier : si je fais (Int 2) + (Int 3), le typage m'affirme que le type résultant est "num". Ce qui a deux conséquences méchantes : 1] je vais avoir besoin de caster le résultat, et les casts, c'est MAL.
Mais pourquoi tu veux caster ?? il suffit de n'utiliser toujours que des nums, si c'est bien fait c'est parfaitement transparent et tu n'as pas à savoir si dedans y a un float un int ou un je sais pas quoi... et comme ça si le type qui fait la lib num modifie des trucs ça ne change rien pour toi happy

Si tu ne veux *que* des nums, alors tu y perds :
* perte d'efficacité lié aux tests de type
* conso mémoire x3
* problèmes de typage : si je veux faire une fonction "divides" qui test si un entier divise un autre, je vais devoir faire des tests de type dynamiques et non plus statiques : là pour le coût ça devient casse-gueule, parce que le moindre float qui vient s'immiscer chez moi est capable de "teinter" tous mes entiers et donc de faire foirer "divides". Sans que j'ai aucun moyen de prédire ça. Bref, finalement, avec un typage aussi restrictif, on en vient à devoir faire des concessions gigantesques au typage (considérer que statiquement, typeof int == typeof float couic), et les vérifications statiques de Caml qu'on nous a tant vantées volent en éclats. Moi je veux juste qu'un int soit un sous-type strict de float.
2] même si je n'ai pas nécessairement besoin de casts (par exemple je veux juste imprimer), je vais avoir une perte importante de performance lié au boxing des valeurs et au test dynamique sur le type (je voudrais un test statique)
euh le test dynamique sur le type c'est un match tout bête, ça m'étonnerait un peu que ça influe sur les performances...

trifus
En C, ça reviendrait à faire un switch en plus. Et les surcharges du C++ se font de manière statique, pas dynamique.
mais si tu veux un typage statique, ça veut dire que tu sais exactement au moment où tu écris le code ce qui va être des float et ce qui va être des int, non ? et si c'est le cas je ne comprends pas pourquoi tu te prends la tête à vouloir des conversions automatiques, tu n'en as pas besoin neutral

Mais on n'en a jamais *besoin*, sauf que tout l'intérêt du système d'inférence de types de CAML est de ne *pas* avoir à écrire explicitement les types. Si je suis obligé à chaque utilisation de la variable de spécifier les types de départ et d'arrivée des casts, finalement je perds plus (pour des exemples pas trop triviaux) que si je déclare juste une fois les types de départ pour chaque variable.

float myfunc(int x,int y,float yk) {
  return (x-y)/(x+y+k);
}

vs
let myfunc x y =
  float_of_int (x-.y)/.(float_of_int x+.float_of_int y+.k)


C'est-à-dire que finalement, ta solution correspond à :

int -> int -> num
int -> float -> num
float -> int -> num float -> float -> num
oui, enfin c'est plutôt num->num->num où num est soit un float soit un int en interne...

Je te dis ce que ta solution représenterait pour le typage si on les représentait par des overloads, pour bien montrer la différence avec The Right Thing...
et moi je voudrais

int -> int -> int
int -> float -> float
float -> int -> float float -> float -> float

Mais si c'est juste ça que tu veux, tu peux utiliser des conversions explicites (comme ça, au moins, tu sais ce que tu fais... sinon tu aurais vite fait de te retrouver avec des float partout pour un petit oubli et de pas comprendre ce qui t'arrive couic)

Effectivement, le typage de Caml ne te donne aucune indication quant au pourquoi du typage d'une variable, et c'est très dommage. Je suis sûr qu'on peut arriver à bien mieux (par exemple, je pourrais sélectionner un type, le modifier [dans ton cas int au lieu de float], balancer au truc d'inférence de type, et il pourrait me dire où ça foire).
Et puis ce que tu proposes passe encore pour l'addition, mais comment tu vas faire pour la division ? happy

Comment ça ?

« The biggest civil liberty of all is not to be killed by a terrorist. » (Geoff Hoon, ministre des transports anglais)

24

Sally
:
Non. On peut parfaitement faire en sorte dans l'IDE que si on laisse la souris sur le + ou si on fait F12 qd on est dessus, on voit le type effectivement utilisé.

Euh oui enfin personnellement ça me semble un chouïa inutilement compliqué neutral

Compliqué, peut-être, inutilement compliqué, non. C'est hyper utile si tu veux te déplacer dans un gros projet, de toute façon.

« The biggest civil liberty of all is not to be killed by a terrorist. » (Geoff Hoon, ministre des transports anglais)

25

le moindre float qui vient s'immiscer chez moi est capable de "teinter" tous mes entiers

oh là là là là...
to taint, v.t. : gâter, infecter, polluer, vicier, souiller happy
et quand je disais n'utiliser que des nums, je voulais dire : au sein d'une application donnée qui a besoin d'en utiliser, évidemment pas toujours et quoi qu'on veuille faire triso. C'est-à-dire : là où tu veux utiliser des nums, tu ne fais pas de conversions, tu n'utilises que ça ; mais sinon tu utilises le type adapté.
De toute façon j'avais mal compris ton exemple, je croyais que tu ne savais pas a priori de quel type allaient être tes variables (typage dynamique quoi).
Mais on n'en a jamais *besoin*, sauf que tout l'intérêt du système d'inférence de types de CAML est de ne *pas* avoir à écrire explicitement les types. Si je suis obligé à chaque utilisation de la variable de spécifier les types de départ et d'arrivée des casts, finalement je perds plus (pour des exemples pas trop triviaux) que si je déclare juste une fois les types de départ pour chaque variable.

moui m'enfin si tu t'en sers beaucoup déjà tu fais let iof = int_of_float (voire pourquoi pas un truc du genre let (!) = int_of_float) et tout de suite tu perds plus grand chose happy
Et puis ce que tu proposes passe encore pour l'addition, mais comment tu vas faire pour la division ?

Comment ça ?

Ben avec la multiplication ou l'addition c'est relativement cohérent parce que l'addition sur les entiers peut être considérée comme la restriction aux entiers de l'addition sur les flottants (enfin dans une certaine mesure), mais pour la division les deux opérations sont complètement différentes, donc je vois pas trop comment tu pourrais définir des surcharges ou transtypages implicites de façon cohérente pour la division smile
avatar
« Le bonheur, c'est une carte de bibliothèque ! » — The gostak distims the doshes.
Membrane fondatrice de la confrérie des artistes flous.
L'univers est-il un dodécaèdre de Poincaré ?
(``·\ powaaaaaaaaa ! #love#

26

Pollux
:
Hippopotamu
:
Pollux
: On parle juste de plonger là-dedans, oui ça n'a pas les mêmes propriétés et alors ? Du moment que c'est injectif, pas de problème...
Ben justement, ce n'est pas forcément injectif.
Si la taille est supérieure, si.

Mais justement, ça dépend de plein de trucs, dont l'architecture. C'est complètement confusogène.
Et même pire : il n'y a aucune propriété de morphisme!
Et alors ?

ben c'est crade.

Je comprends ce choix, mais c'est *vraiment* un truc que je trouve complètement crade.
Et après on se retrouve avec des purs problèmes de torture mentale quand on doit déterminer ce que fait vraiment le code...
Non. On peut parfaitement faire en sorte dans l'IDE que si on laisse la souris sur le + ou si on fait F12 qd on est dessus, on voit le type effectivement utilisé.

Là tu te rabats sur l'IDE, ça n'a rien à voir avec la question du langage.non


Sinon, bizarrement, toutes les choses que tu sembles apprécier (surcharge, convertion automatique), je trouve ça crade et inutile. Question de goût ?!

Quand tu dis "Moi je veux juste qu'un int soit un sous-type strict de float.", ben moi je veux exactement le contraire. Beurkkk !!

Et j'ai surtout l'impression que tes exigences ne sont pas vraiment justifiées dans l'absolu, mais ne sont justifiées que pour se rapprocher du C.

let myfunc x y = 
  float_of_int (x-.y)/.(float_of_int x+.float_of_int y+.k)

Erreur de type dans cette fonction !! tongue
Les droits inaliénables du troll :
1) le droit d'avoir raison
2) le droit d'être péremptoire
3) le droit de ne pas lire
4) le droit de ne pas répondre
5) le droit d'être de mauvaise foi
6) Autant pour moi / Faignant / Vivent Tintin et Milou

27

Sally
:
le moindre float qui vient s'immiscer chez moi est capable de "teinter" tous mes entiers

oh là là là là...
to taint, v.t. : gâter, infecter, polluer, vicier, souiller happy

D'où les guillemets trigic
et quand je disais n'utiliser que des nums, je voulais dire : au sein d'une application donnée qui a besoin d'en utiliser, évidemment pas toujours et quoi qu'on veuille faire triso. C'est-à-dire : là où tu veux utiliser des nums, tu ne fais pas de conversions, tu n'utilises que ça ; mais sinon tu utilises le type adapté. De toute façon j'avais mal compris ton exemple, je croyais que tu ne savais pas a priori de quel type allaient être tes variables (typage dynamique quoi).

Oui bon je suis d'accord que si on avait besoin de typage dynamique on aurait besoin de truc plus délicats et que ta solution ne serait pas si mauvaise au vu de ce qu'on demande. Mais moi je me place dans le cadre du typage statique...
Mais on n'en a jamais *besoin*, sauf que tout l'intérêt du système d'inférence de types de CAML est de ne *pas* avoir à écrire explicitement les types. Si je suis obligé à chaque utilisation de la variable de spécifier les types de départ et d'arrivée des casts, finalement je perds plus (pour des exemples pas trop triviaux) que si je déclare juste une fois les types de départ pour chaque variable.

moui m'enfin si tu t'en sers beaucoup déjà tu fais let iof = int_of_float (voire pourquoi pas un truc du genre let (!) = int_of_float) et tout de suite tu perds plus grand chose happy

Bof... Et en plus un autre effet pervers de ces conversions explicites, c'est qu'on est tenté de limiter les nuances de typage au strict minimum : par exemple ça pourrait être une bonne idée de distinguer entre "string", "FileName" et "URL" (même si en mémoire c implémenté pareil), ça évite les erreurs (par exemple ils pourraient être tous les trois convertibles en "ReadableData")
Mais en fait p-ê que l'orientation objet permet ça ? (et est-ce que ça serait possible pour les types de base ?)
Et puis ce que tu proposes passe encore pour l'addition, mais comment tu vas faire pour la division ?
Comment ça ?

Ben avec la multiplication ou l'addition c'est relativement cohérent parce que l'addition sur les entiers peut être considérée comme la restriction aux entiers de l'addition sur les flottants (enfin dans une certaine mesure), mais pour la division les deux opérations sont complètement différentes, donc je vois pas trop comment tu pourrais définir des surcharges ou transtypages implicites de façon cohérente pour la division smile

Ah, oui, ce n'est de toute façon pas un morphisme, ça c'est clair. Même pour l'addition, d'ailleurs, si on suppose que (-2^30)+(-2^30)=0 ...

« The biggest civil liberty of all is not to be killed by a terrorist. » (Geoff Hoon, ministre des transports anglais)

28

Hippopotamu
:
Pollux
:
Hippopotamu
:
Pollux
: On parle juste de plonger là-dedans, oui ça n'a pas les mêmes propriétés et alors ? Du moment que c'est injectif, pas de problème...
Ben justement, ce n'est pas forcément injectif.
Si la taille est supérieure, si.
Mais justement, ça dépend de plein de trucs, dont l'architecture. C'est complètement confusogène.

Oui, c'est injectif pour les valeurs gentilles ^^ De toute façon pour les valeurs méchantes, ça dépend de tte façon de l'architecture, donc c moins un pb smile
Et même pire : il n'y a aucune propriété de morphisme!
Et alors ?

ben c'est crade.
Je comprends ce choix, mais c'est *vraiment* un truc que je trouve complètement crade.

Pas moi... Pour moi c juste un petit raccourci d'écriture... (et c très lisible avec un peu d'habitude)
Et après on se retrouve avec des purs problèmes de torture mentale quand on doit déterminer ce que fait vraiment le code...
Non. On peut parfaitement faire en sorte dans l'IDE que si on laisse la souris sur le + ou si on fait F12 qd on est dessus, on voit le type effectivement utilisé.

Là tu te rabats sur l'IDE, ça n'a rien à voir avec la question du langage.non

Mhû ? De toute façon, The Right Thing n'est certainement pas d'avoir un compilo autiste qui ne communique rien à l'IDE... Et je ne suis pas sûr que le format text-only édité seulement par un éditeur de texte qui ne connaît rien au langage, et qui ne soit jamais modifié en retour par le compilo, soit immortel...
Sinon, bizarrement, toutes les choses que tu sembles apprécier (surcharge, convertion automatique), je trouve ça crade et inutile. Question de goût ?!

Question de concision... Mais c vrai que c'est p-ê aussi un petit peu casse-gueule sachant que les types des variables ne sont affichés nulle part en Caml... il faudrait que le compilo puisse mettre les types inférés dans le fichier source, ou un truc du style...
Quand tu dis "Moi je veux juste qu'un int soit un sous-type strict de float.", ben moi je veux exactement le contraire. Beurkkk !!

Tu veux que int soit un sur-type au sens large de float ? trigni
Et j'ai surtout l'impression que tes exigences ne sont pas vraiment justifiées dans l'absolu, mais ne sont justifiées que pour se rapprocher du C.

Mhû ? y a pas de surcharge (sauf les prédéfinies) en C, donc ce serait plutôt C++... Et des surcharges limitées au prédéfinies ne me satisferaient pas non plus ^^ (même si ça serait légèrement moins chiant)
let myfunc x y = 
  float_of_int (x-.y)/.(float_of_int x+.float_of_int y+.k)

Erreur de type dans cette fonction !! tongue

arf ^^ j'avais d'abord écrit la fonction, puis je me suis rendu compte que j'avais oublié les . => j'en ai mis un de trop...

« The biggest civil liberty of all is not to be killed by a terrorist. » (Geoff Hoon, ministre des transports anglais)

29

Pollux :
Tiens, ça marche si on rajoute en plus de la racine carrée la fonction Gamma (G(n)=(n-1)!), de 1 à 150...

Moumou> en fait j'ai commencer par chercher certains nombres manquants, puis je me suis rendu compte que c'était pas possible, donc je me suis dit que j'allais faire un prog pour voir si c'était moi qui étais polio ou si l'énoncé était faux... J'ai donc fait un chti prog en Caml smile (et ne dites pas "Caml beurk", parce que c vraiment plus rapide à faire en Caml qu'en C) Si ça vous intéresse je peux nettoyer un peu le code et faire un vrai formattage des résultats (gestion des parenthèses digne de ce nom)

Tu ne peux plus nier l'évidence maintenant hehe
avatar
I'm on a boat motherfucker, don't you ever forget

30

love
avatar
« Le bonheur, c'est une carte de bibliothèque ! » — The gostak distims the doshes.
Membrane fondatrice de la confrérie des artistes flous.
L'univers est-il un dodécaèdre de Poincaré ?
(``·\ powaaaaaaaaa ! #love#