1

J'ai installé Ocaml depuis peu et ayant toujours utilisé caml light, je suis en pleine migration ^^

Donc, ma question principale ce serait : quelle est la difference *dans le fond* entre classes et modules (ouais je debute aussi en OO aussi donc ...) ? Qu'est-ce qu'on peut faire dans l'un et pas dans l'autre ?
(ah oui et question encore plus "profonde" : qu'est-ce qui est si revolutionaire dans la POO ? les classes c'est juste des struct en mieux ... ^^)

J'ai remarqué aussi qu'il y plein de contraintes de majuscules sur les noms/constructeurs de types/modules/classes, je trouve ca un peu dommage :/

(si c'était possible que ce topic parte pas en trollodrome (en tout cas pas tout de suite, jvous dirais quand c'est bon happy) ca serait cool merci)

2

Nheryvra :
qu'est-ce qui est si revolutionaire dans la POO ?

tu ne réfléchis plus en termes d'actions, mais d'objets (oué, ok, je sors ^^)

plus sérieusement, en langage fonctionnel, c'est "on" (Dieu tout puissant quoi) qui effectue des actions sur des entités
genre, do_ceci_cela(ma_structure)
le point de vue est centré sur les fonctions, sur ce qui est fait

en langages Orientés Objets, c'est l'inverse : tu as des entités (les objets), et ce sont ces entités qui font ou qui subissent ce que leur font d'autres entités
en somme, tout est bien identifié
par exemple : ecran.affiche("glop");
(en fonctionnel ça serait affiche(écran, "glop"); et rien ne dirait que la fonction affiche est spécifique aux objets de classe écran)

d'une certaine façon, l'OO est plus proche de la vie réelle : dans la vie réelle, tu as les Hommes qui interviennent sur les Arbes, à l'aide de la méthode couper (par exemple ^^)

Aussi, l'OO introduit des notions telles que l'héritage ; par exemple, un saule est un arbre, qui est un végétal
l'arbre a toutes les propriétés du végétal (feuilles, chlorophyle), plus certaines qui lui sont propres (tronc)
et le saule a toutes les propriétés de l'arbre (et donc, du végétal), plus d'autres qui lui sont propres, que n'ont pas les autres arbres

enfin, bon courage quoi grin
avatar
Tutorial C (TI-89/92+/v200) - Articles Développement Web (PHP, Javascript, ...)
« What is the sound of Perl? Is it not the sound of a wall that people have stopped banging their heads against? » - Larry Wall

3

ouais je vois bien tout ca, c'est le speech "officiel" ^^ en gros c'est juste un style different, ca ne *fait* rien de nouveau (je dis pas que c'est mal hein ^^)

4

heu... l'héritage, ça apporte beaucoup, quand tu commences à savoir l'utiliser
avatar
Tutorial C (TI-89/92+/v200) - Articles Développement Web (PHP, Javascript, ...)
« What is the sound of Perl? Is it not the sound of a wall that people have stopped banging their heads against? » - Larry Wall

5

squale92 :
heu... l'héritage, ça apporte beaucoup, quand tu commences à savoir l'utiliser

Ca apporte beaucoup, a plusieurs niveau : conception, maintenabilité, réutilisation, etc...
Mais on a pu faire sans, et on peut toujours faire sans, ce n'est pas "indispensable" wink
Ce qu'il veut dire, c'est que l'objet n'apporte rien en terme de potentiel fonctionnel pour la réalisation d'un programme, ce qui est ma foi vrai smile
Mon site perso : http://www.xwing.info

6

en terme de potentiel fonctionnel, non, en effet, l'objet n'apporte rien
mais en terme de rapidité/facilité de conception/réalisation (quand tu connais déjà un peu les bases suffisant à faire quelque chose de propre, et que tu prends le temps de réfléchir), l'objet aide - à mon avis, bien entendu
avatar
Tutorial C (TI-89/92+/v200) - Articles Développement Web (PHP, Javascript, ...)
« What is the sound of Perl? Is it not the sound of a wall that people have stopped banging their heads against? » - Larry Wall

7

on est d'accord

8

toutafé chinois
Mon site perso : http://www.xwing.info

9

Il y a pas "plein de contraintes", il y a que les noms de modules et de constructeurs, et seulement eux, doivent commencer par des majuscules. Enfin je sais pas ce que t'appelles "plein" mais bon cheeky.
euh les idées de l'orienté-objet sont déjà dans les modules sinon (enfin l'encapsulation tout ça) (je réponds vite fait hein, pq déjà ça fait super longtemps que j'ai pas touché à ça et ensuite j'ai du taff par-dessus la tête), mais il y a des différences. Même si tout ce qui est faisable avec les objets est probablement aussi faisable sans. Bon entre autres il y a l'héritage et les méthodes virtuelles, les modules n'ont pas d'équivalent il me semble (?)

Mais la différence principale je pense (enfin de mon point de vue) c'est que si tu prends le type Machin.t et le type Truc.t, le seul type générique qui les recouvre tous les deux, c'est 'a, donc tu ne peux pas définir une fonction qui prend comme argument une valeur de l'un ou l'autre de ces deux types mais pas autre chose ; si tu veux le faire tu dois définir un type bidon = Machin of Machin.t | Truc of Truc.t
et genre si tu veux convertir ta valeur en chaîne tu dois te taper un match bidon etc.

alors qu'avec les objets, tu peux écrire une fonction du genre let print_nimportequoi x = print_endline x#to_string
et ta fonction aura un type générique qui lui permet de prendre en argument n'importe quel objet qui possède une méthode to_string.
Tu peux d'ailleurs définir plusieurs classes différentes qui ont *exactement* le même type (c'est-à-dire qui ont des méthodes de noms et de types identiques) mais des contenus complètement différents. Et les objets de ces deux classes sont indiscernables.

Sinon ce qu'on ne peut pas faire avec les classes et qu'on peut faire avec les modules, euh, j'ai pas d'idée là et puis faut que j'aille me coucher cheeky.


Edit : je me pose des questions qui n'ont aucun sens (mais bon faut voir l'heure aussi embarrassed )
[digression]
quand tu commences à faire des :
class bidule = let [...] in function x -> let [...] in object [...] end
ça devient chaud de savoir quoi change quand (oui parce que par exemple dans le premier [...] tu peux mettre une ref qui est modifiée dans le second [...] et l'est de nouveau dans le troisième [...]) ET aussi tu peux instancier plusieurs fois la classe, genre :
let cree_bidule = new bidule and cree_bidule_aussi = new bidule in let a = cree_bidule () and b = cree_bidule () and c = cree_bidule_aussi () and d = cree_bidule () and e = cree_bidule_aussi ()

tu peux tester avec un truc comme « class bidule = let compteur = ref 0 in function () -> let numero = incr compteur; !compteur in object method numero = numero end »
si tu veux (bon y a pas besoin de tester pour comprendre ce que ça fait hein, je dis ça comme ça ^^)[/digression]
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#

10

Sally :
Il y a pas "plein de contraintes", il y a que les noms de modules et de constructeurs, et seulement eux, doivent commencer par des majuscules. Enfin je sais pas ce que t'appelles "plein" mais bon mod.gif .

mouais c'est pas vraiment plein, je me suis juste fait avoir pas mal de fois avant de comprendre ^^ toujours est-il que je comprend pas vraiment pourquoi ces restrictions ...
Sally :
Sinon ce qu'on ne peut pas faire avec les classes et qu'on peut faire avec les modules, euh, j'ai pas d'idée là et puis faut que j'aille me coucher mod.gif .

bn happy c'est pas tant de savoir comment faire l'equivalent de l'un en l'autre par tout les moyens mais de voir les "différences profondes" ^^

11

merci ^^
(j'ai rajouté plein de trucs dans mon post)
(je vais vraiment me coucher)
(je connais pas les « différences profondes », enfin ce que j'ai dit je trouvais que c'en était une)
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#

12

Sally :
(j'ai rajouté plein de trucs dans mon post)

ah ouais effectivement grin
Sally :
(je connais pas les « différences profondes », enfin ce que j'ai dit je trouvais que c'en était une)

hum, j'avoue avoir lu un peu en diagonale, mais tu n'a parlé que des différences entre plusieurs modes de definition de classes, moi je me demandais les différences entre modules et classes ^^

13

Dans ce que j'ai rajouté je n'ai parlé que des différences entre plusieurs modes de definition de classes, mais dans ce que j'avais mis avant (le coup des types génériques) non, et à mon avis c'est une différence profonde... enfin en tous cas c'est le truc qui pootre le plus dans l'extension objet de caml ^^
(bn vraiment sissi triso)
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#

14

ah ouais j'avais effectivement mal lu la premiere partie de ton post ^^ à vrai dire je demandais ca parce que les classes ca m'avait l'air pas mal et les modules bien lourds (surtout tout ce qui est signature et foncteurs) donc je me demandais si je perdais beaucoup a ne pas m'y interesser (dans un premier temps) happy

15

Bon je pense que dans un premier temps tu n'as pas trop besoin de t'intéresser aux modules paramétriques, ni aux déclarations de sous-modules à l'intérieur d'un fichier (les module = ... end). Enfin à moins que tu utilises un des modules de la bibliothèque standard qui prend des modules en paramètres smile (style Set ou Hashtable il me semble). En fait les modules c'est plus adapté aux trucs plus gros ; et dans un module tu as accès à tout caml (tu peux en particulier définir des types et des classes) et exporter très précisément juste ce que tu veux.
En fait moi j'ai pris l'habitude de mettre mon code dans plusieurs fichiers différents, avec les trucs qui vont ensemble dans un même fichier, et de toujours écrire un fichier signature parce que j'ai toujours plein de valeurs globales et de fonctions qui ne sont pas censées être utilisées par le reste, mais je ne suis pas allé beaucoup plus loin jusqu'à récemment. (ie je n'utilisais que les .ml/.mli, pas les module = ...)
Mais bon dans un premier temps rien ne t'empêche de ne pas te soucier du .mli puisqu'il t'en génère un automatiquement. Tu peux aussi le générer et le modifier ensuite. Ou tu peux partir des réponses du toplevel pour écrire la signature...

"Récemment" c'est quand j'ai fait ça : http://home.tele2.fr/grinstelbes/othello_klein.ml (cf. topics/35364-les-mots-me-manquent ). En fait j'ai défini des modules avec dans l'idée que je pourrais définir plusieurs types de plateaux (pas seulement la bouteille de Klein) et que dans ce cas j'aurais pas à changer le reste du code, juste à open un module différent happy. Je ne suis pas sûr du tout que ce soit la dernière version du code sur le site, mais flemme de chercher sur mon disque embarrassed. Enfin c'était surtout pour teste le principe, mais j'ai pas eu le temps d'aller plus loin pour l'instant en fait.
Euh sinon les modules ça existait déjà dans caml-light non ??
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#

16

en caml light j'utilisais les .ml/.mli aussi mais on pouvait pas definir un module à l'interieur d'un fichier

autre question, on ne definiti pas de type (logiquement) à l'interieur d'une classe ? comment on peut faire des classes "recursives"? par exemple comment on ferait une classe Liste ?

17

Tu veux le faire avec une val mutable ? En fait le truc c'est que quand tu définis ta val il faut lui donner une valeur initiale. Si tu veux qu'il puisse ne pas y avoir de valeur, le plus simple c'est d'utiliser une option, et dans ce cas tu initialises à None. Par contre le problème c'est que None est de type 'a option et que si c'est une classe non paramétrique tu ne peux pas avoir des val de types polymorphiques, bien sûr, donc il faut lui préciser le type, comme ça :

class bidule = object val mutable truc = (None : bidule option) [...] end

Si tu ne veux pas qu'il puisse ne pas y avoir de valeur, ben il faut que t'initialises avec un objet ; alors t'en as un sous la main, c'est bibi (l'objet que tu es en train de construire. Tu peux lui donner le nom que tu veux mais j'aime bien bibi). Mais en fait quand tu définis une classe bidule ça définit plusieurs types : le type bidule, qui est le type des objets qui ont exactement toutes les méthodes publiques de la classe, et aussi le type #bidule, qui est le type des objets qui ont *au moins* toutes les méthodes publiques de la classe. Et bibi, il est de type #bidule, parce que la classe elle peut être étendue par héritage, donc en fait on ne connaît pas son type exact à bibi. La solution c'est la coercition (je sais pas comment ça s'appelle en français en fait triso. Bon à vrai dire même en anglais je sais pas exactement), c'est de le forcer à appartenir au type bidule, en masquant les méthodes en trop. Ça se fait comme ça : :> bidule
Donc tu peux écrire :

class bidule = object (bibi) val mutable truc = (bibi :> bidule) [...] end

Bon si tu ne veux *pas* utiliser une val mutable, c'est que tu as l'intention de passer la val en argument, mais là ça ne pose pas trop de problème (enfin il y a intérêt à ce que l'argument soit une option, sinon ça veut dire qu'il faut un objet pour construire un objet, c'est pas super cheeky. Enfin pourquoi pas remarque hein. On pourrait toujours s'en sortir : let rec a = new bidule b and b = new bidule a ça marcherait. C'est peut-être utile dans certains cas. Mais bon dans le cas général :
class bidule ?(x : bidule) () = object val truc = x [...] end
ça doit faire ce qu'il faut je pense 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#

18

en fait c'est directement possible d'avoir un champ non initialisé dans une classe ?

en fait je crois que je prend le probleme de travers, yaurait moyen que tu me montre comment tu ferais une classe Liste (qui fait des listes à la caml) pour voir si vraiment je fais fausse route dans ce que je pense ?

19

ben non tu ne peux pas avoir de champ non initialisé, ça n'existe pas... tu peux avoir un champ de type 'a option c'est tout.
(la définition c'est type 'a option = None | Some of 'a)

comment je ferais une classe liste, ben ça dépend, un truc du genre 'a list ? avec des classes ?

[digression]

Bon j'utilise des arguments optionnels. En fait un argument optionnel de type 'a ça donne une valeur de type 'a option, qui vaut None si tu n'as pas mis l'argument et Some x si tu as mis x comme argument. Le truc avec les fonctions qui prennent des arguments optionnels c'est qu'elles doivent aussi prendre au moins un argument obligatoire après ces arguments optionnels (souvent l'argument obligatoire est ()), pour que le compilateur sache si tu as appliqué la fonction en entier ou pas.
Donc si j'écris let f ?x () = match x with None -> 0 | Some blurb -> blurb
ensuite si je tape f c'est la fonction, mais si je tape f () c'est l'application de la fonction sans l'argument optionnel, donc en l'occurrence c'est la valeur 0 (ie c'est quand le compilateur rencontre l'argument obligatoire qu'il remplace tous les arguments optionnels qui n'ont pas été donnés par None, et il applique la fonction à ce moment-là).
Bon ensuite il y a une syntaxe particulière parce que les arguments optionnels c'est des arguments nommés (on peut aussi faire des arguments nommés non optionnels), c'est-à-dire que je ne peux pas faire f 2 par exemple, je dois faire f ~x:2 ou, plus joli, let x = 2 in f ~x

Ah et à part ça tu peux faire des arguments optionnels qui ne sont pas des options mais qui ont une valeur par défaut : let f ?(x = 0) () = x est équivalent à ce que j'ai écrit plus haut.

[/digression]

Bon voici comment je ferais une liste (ça n'a aucun intérêt évidemment) :

class ['a] liste ?(queue: 'a liste option) (tete: 'a) = object
  method tete = tete
  method queue = queue
  method to_list = tete :: match queue with None -> [] | Some x -> x#to_list
end

let concat tete queue = new liste ~queue tete

let superliste = concat 1 (concat 2 (concat 3 ( concat 4 ( concat 127000 (new liste 17)))));;

superliste#to_list;;
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#

20

Sinon j'ai aussi des exemples d'utilisation des objets dans [Jeu] i++ si ça t'intéresse trioui
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#

21

(ouais ca m'interesse ^^)
ah c'est pas mal les options, effectivement je connaissais pas ...

bon pour faire mon chieur jusqu'au bout, est-ce qu'il serait possible de faire une classe liste qui ne prend jamais aucun argument, ie pour faire genre
class ['a] liste = object (...) end ;;
faudrait mettre quoi à la place de (...) ? ^^

22

(ouais ca m'interesse ^^)
T'as plus qu'à retrouver le topic et la page cheeky

Pour ta liste :

class ['a] liste = object (bibi)
  val debut = None
  val suite = None
  method est_vide = debut = None
  method ajoute_en_tete (x : 'a) = {<debut = Some x; suite = Some bibi>}
  method queue = match suite with Some suite -> suite
  method tete = match debut with Some debut -> debut
end


Edit : en fait je suis trop con c'est précisément ça la grosse différence que je trouvais pas entre les val et les variables locales triso
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

aaaaaaaah voila c'est ca que je voulais faire love merci ^^

24

!up

Je me posais une question con, dont la réponse me semblait auparavant évidente, mais plus maintenant. Pourquoi en caml, int (resp char) n'est pas un sous-type de float (resp string) ?
avatar
I'm on a boat motherfucker, don't you ever forget

25

Ben la multiplication et l'addition sont les mêmes mais pas la division, et tu ne peux pas réduire modulo un flottant... n'oublie pas qu'un flottant ne représente pas vraiment un nombre mais une approximation, donc un entier et le même entier considéré comme un flottant ce n'est pas exactement la même chose, ni dans l'implémentation ni même conceptuellement. Pour les chaînes de caractères flemme de réfléchir, c'est l'heure de dormir oui

edit : je dis n'importe quoi (je ne pensais qu'à des *petits* entiers en fait) : la multiplication et l'addition non plus ne sont pas les mêmes, pour les entiers elles sont modulo max_int, et pour les flottants elles ne sont pas en précision infinie, bref ça n'a carrément aucun rapport en fait...
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

Oui, mais tu me parles des opérations, pas des types en eux même. Je sais bien que les opérations ne sont pas les mêmes, et que la distinction en caml entre / et /. est nécessaire.

Quand je parle de sous-typage, je veux savoir pourquoi une opération qui prend en argument des float ne pourrait pas prendre des int ? C'est ce que j'entends par sous-typage. Je ne comprends pas pourquoi caml ne remplacerait pas statiquement et automatiquement a /. b avec a : int et b : int par (float_of_int a) /. (float_of_int b). C'est pour des raisons pratiques, politiques, ou conceptuelles ?
avatar
I'm on a boat motherfucker, don't you ever forget

27

le principe de sous-typage n'existe pas en ocaml, (et il serait difficilement implémentable, car c'est un peu le meme principe de typage que la surcharge qui change pas mal le système de types, et qui a déja regardé au moins une fois le typeur d'ocaml a très vite compris qu'on ne peut rien y ajouter sans tout casser...)
attention, ça pourrait etre faisable avec un peu d'effort...

ensuite, pourquoi caml ne remplace pas statiquement:
ok, dans ce cas ce n'est pas du sous typage, c'est de la macro-génération, tu peux faire ça facilement avec un préprocesseur comme camlp4

par exemple:

type num = Intnum of int | Floatnum of float;;

let numplus a b =
match a with
Intnum x -> (
match b with
Intnum y ->
Intnum ( x + y)
| Floatnum y ->
Floatnum ( (float_of_int x) +. y)
)
| Floatnum x -> (
match b with
Intnum y ->
Floatnum ( x +. (float_of_int y))
| Floatnum y ->
Floatnum ( x +. y)
)

etc...
et puis tu remplaces tous les int et floats par des num avec le constructeur approprié, tu remplaces les opérateurs par ceux ci, et quand tu les utilise dans des fonctions typées pour les types de base, tu elimine la disjonction par un match avec conversion si c'est le mauvais type, ...

avec ça tu peux meme faire de la surcharge version sucre syntaxique en réécrivant des fonctions, ex:

let print n =
match n with
Intnum x -> Pervasives.print_int x
| Floatnum x -> Pervasives.print_float x

donc ben si t'as vraiment envie de ça, ben t'as qu'à réécrire ça pour tous les types et en englobant la lib standard ocaml happy
avatar
fabetal_ > Hier, je me suis fait monter par un pote
redangel > et en chevals, ça donne quoi?
Nil> OMG I think I'm gay

28

BookeldOr
: le principe de sous-typage n'existe pas en ocaml

int list n'est donc pas un sous-type de 'a list ? hum
avatar
I'm on a boat motherfucker, don't you ever forget

29

BookeldOr :
type num = Intnum of int | Floatnum of float;;

let numplus a b =
  match a with
    Intnum x -> (
    match b with
      Intnum y ->
        Intnum  ( x + y)
   | Floatnum y ->
        Floatnum ( (float_of_int x) +. y)
    )
 | Floatnum x -> (
    match b with
      Intnum y ->
        Floatnum ( x +. (float_of_int y))
   | Floatnum y ->
        Floatnum ( x +. y)
    )

let print n =
 match n with
    Intnum x -> Pervasives.print_int x
 | Floatnum x -> Pervasives.print_float x

(Je vais répondre mais pour l'instant je veux juste lire le post de Bookeldor tongue)
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#

30

Moumou > bon je réponds d'abord à la nouvelle question. Non c'est 'a list qui est un sous-type de int list je crois. J'arrête pas de m'embrouiller avec ça à vrai dire c'est tordu mais... oui c'est ça. Si tu prends une valeur de type int list, tu ne peux en général pas la considérer comme une valeur de type 'a list. Par exemple [ 3 ] n'est pas de type 'a list. En revanche si tu prends une valeur de type 'a list, tu peux toujours la considérer comme une int list : c'est facile, il n'existe qu'une seule valeur de type 'a list et c'est [].

En fait il est faux que ocaml ne connaît pas le sous-typage, ce que veut dire BookeldOr c'est que ce sous-typage n'est jamais implicite. On m'a dit tout à l'heure qu'il n'était pas possible d'avoir à la fois le sous-typage implicite et l'inférence de types à la caml (je sais pas trop si c'est juste des questions de complexité d'algorithme ou si c'est plus profond que ça comme raisons). Mais tu as l'opérateur de coercition (je sais pas si on dit comme ça en français grin) qui force à considérer la valeur du sous-type comme une valeur du super-type. Exemple :
# ([] :> int list);;
- : int list = []
# ([1] :> 'a list);;
- : int list = [1]
# (`a :> [`a|`b]);;
- : [ `a | `b ] = `a
Tiens intéressant le deuxième résultat. Ah ben oui, en fait tu peux considérer [ 1 ] comme une 'a list à condition que 'a soit égal à int en fait, donc 'a list est bien d'un certain point de vue un sous-type de int list, mais sauf que c'est seulement pour une certaine valeur de 'a, c'est pas le type 'a list avec la variable libre...

bon je réponds à l'autre question dans un autre post ^^
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#