|
Publicité ' | ||||||||||||||||||||||||
|
|
#181 | ||
![]() ![]() Inscription : juin 2006 Messages : 6 935 ![]() |
Effectivement, si l'on utilise la fonction max avec un objet qui ne peut pas utiliser l'opérateur <, cela va provoquer une erreur de compilation (j'ai du adapter le code pour le faire fonctionner avec des classes et pas uniquement avec les types primitifs) :
Code C++ :
Il y a une erreur au niveau de la ligne : const A & amax= max... Pour Java, en fait, il n'y a qu'un code "exécutable" (bytecode plutôt) dont tous les paramètres sont toujours : Object (car toutes les classes héritent d'Object). Il n'y a qu'une vérification des types passées lors de la compilation (et non de l'exécution).
__________________
Je ne répondrai à aucune question technique en privé |
||
|
|
00
|
|
|
#182 |
|
Membre éclairé
![]() ![]() Inscription : août 2005 Messages : 417 ![]() |
Okay, donc il me semble que le sens du template que tu donnes en exemple est:
``Pour tout type T pour lequel il existe une fonction '<' de type T*T -> Bool, ....'' C'est à dire qu'en fait la quantification sur le type T est bien universelle, mais elle est conditionnée par une seconde quantification (tout aussi implicite, mais existentielle celle-là) sur la fonction < . Cette seconde quantification est de fait une contrainte sur T. Est-elle de même nature que celles dont parlait LLB ?
__________________
Ma page maths. |
|
|
00
|
|
|
#183 | ||||||
|
Membre éclairé
![]() ![]() Inscription : août 2005 Messages : 417 ![]() |
Puisque certains d'entre vous font des essais avec Anubis en essayant (tout à fait légitimement) d'en exhiber les défauts, je me suis dit que je pourrais bien de mon coté en faire autant avec ocaml (par exemple).
J'ai donc tapé ceci dans un fichier: Code :
Code :
Code :
Lequel a raison à votre avis ?
__________________
Ma page maths. |
||||||
|
|
00
|
|
|
#184 |
|
Membre Expert
![]() Inscription : mars 2002 Messages : 962 ![]() |
Je répondrai aux autres messages (s'il y a besoin) plus tard.
Pour cette dernière question : je préfère la solution d'Anubis, puisqu'il comprend tout seul qu'on utilise le type toto. Mais, je trouve ça très amusant. OCaml et Anubis se ressemblent sur le fait qu'ils privilégient par endroit la sûreté et la clarté à l'expressivité (mais les choix qui sont faits en pratique s'opposent en plusieurs endroits). C'est un bon exemple : en Caml, on ne définit pas deux types sommes en utilisant un même identifiant. C'est la même chose pour les structures : les champs doivent avoir un nom différent. Du coup, on peut te retourner la remarque : le fait d'utiliser deux fois le "a" ne réduit-il pas la clarté et la facilité de relecture ? Dans le code, ce n'est pas écrit quel "a" est utilisé : le compilateur le déduit. Je trouve ça étonnant de faire ce choix, alors que l'on refuse le typage implicite aux autres endroits. |
|
|
00
|
|
|
#185 |
|
Membre éclairé
![]() ![]() Inscription : août 2005 Messages : 417 ![]() |
Ce qui m'etonne le plus dans le comportement de ocaml, c'est pourquoi on ne peut pas surcharger un symbole sous pretexte qu'il est un constructeur (tag-name dans le vocable ocaml) ? Je ne vois pas de raison à cette restriction.
Le compilateur Anubis trouve deux interprétations pour a, et comme b n'en a qu'une, il élimine l'une des deux car l'égalité est typée ($T,$T) -> Bool. C'est un effet de l'inférence de types. Par contre, si j'avais mis: le compilateur Anubis aurait protesté que c'est ambigu, car a a deux types possibles, alors que (je viens de faire l'expérience) le compilateur ocaml ne dit plus rien.
__________________
Ma page maths. |
|
|
00
|
|
|
#186 |
|
Membre Expert
![]() Inscription : mars 2002 Messages : 962 ![]() |
Parce que OCaml refuse toute forme de surcharge. Chaque symbole n'a qu'un seul type. 0 est forcément un entier. + est forcément l'addition de 2 entiers. +. est forcément l'addition de 2 flottants.
On peut redéfinir un symbole, mais pas le surcharger (l'ancienne valeur est donc masquée). Certains prétendent que ça améliore la lisibilité et supprime toute forme d'ambiguité, au prix parfois d'une lourdeur syntaxique. Un peu pour la même raison que tu refuses le typage implicite. Edit : d'ailleurs, si les symboles pouvaient être surchargés, cela nécessiterait parfois des annotations de type, comme tu le signales. En Caml, on n'est (quasiment) jamais obligé de typer à la main. |
|
|
00
|
|
|
#187 |
|
Membre éclairé
![]() ![]() Inscription : août 2005 Messages : 417 ![]() |
Voici un exemple qui me semble significatif de l'utilité de pouvoir surcharger les constructeurs (tag-names en ocaml).
Dans le fichier 'library/web/making_a_web_site.anubis', il y a toute une interface avec l'HTML (qui fait qu'on écrit jamais une ligne d'HTML pour faire un site). Dans cette interface, j'ai poussé le vice (si j'ose dire) jusqu'à définir deux types différents pour représenter en Anubis les éléments HTML qui sont dans un formulaire et ceux qui sont hors de tout formulaire. Mon idée était d'obliger à respecter la règle de la norme HTML4 qui interdit d'imbriquer les formulaires. Il y a donc deux types de noms 'HTML_In_Form' et 'HTML_Off_Form', avec chacun de nombreuses alternatives (variants en ocaml), chaque alternative représentant un élément HTML particulier. Bien entendu, l'alternative 'form(...)' figure dans 'HTML_Off_Form', mais pas dans 'HTML_In_Form'. Il y a de nombreux éléments HTML qui peuvent figurer à la fois dans les formulaires et hors des fomulaires: le texte, les images, les tables, ... ce qui fait que mes deux types ont des alternatives écrites exactement de la même façon (avec le même nom en particulier), ce qui ne serait donc pas possible en ocaml. C'est très imortant que ces alternatives soient écrites exactement pareil dans les deux types, car le programmeur, quand il compose sa page HTML n'a pas à se soucier, quand il met du texte ou une image, de savoir s'il est ou non dans un formulaire. Par contre, s'il essaye de mettre un champ de saisie 'input' hors d'un formulaire, il se fait taper sur les doigts par le compilateur. N'est-ce pas un + pour la sûreté ?
__________________
Ma page maths. |
|
|
00
|
|
|
#188 | ||
|
Membre éclairé
![]() ![]() Inscription : août 2005 Messages : 417 ![]() |
Citation:
Citation:
Effectivement, si on typait aussi peu en Anubis qu'en Caml, on aurait plein de choses ambiguës. Il y a donc deux approches différentes.
__________________
Ma page maths. |
||
|
|
00
|
|
|
#189 | ||
![]() ![]() Damien GuichardInscription : juin 2007 Messages : 1 514 ![]() |
Citation:
À propos de l'inférence de type: Anubis ne demande que de 'prototyper' les fonctions. Je crois que les langages fonctionnels à équations (Haskell/Miranda) demandent également de 'prototyper' les fonctions. Je n'ai jamais lu ni sur ce forum ni ailleurs que Haskell ou Miranda ne pratiqueraient pas l'inférence de type. Pour les fonctions anonymes (en Anubis 1.7) il faut donner le type des arguments formels mais pas celui du résultat. À l'usage cela ne m'a pas paru excessivement verbeux, par contre ça responsabilise davantage, avec Caml j'ai un peu tendance à me reposer sur l'inférence de types, par exemple pour l'argument d'un fold_left ou d'un fold_right j'utilise l'interpréteur pour m'éviter de réfléchir, si ça ne passe pas l'inférence de type alors j'échange les deux arguments et je ne m'interroge que si ça ne passe toujours pas. Anubis demande plus d'attention (l'absence d'un interpréteur interactif y est sans doute pour quelque chose). Des versions récentes de C++ et Java possèdent le polymorphisme paramétrique, mais aucun des deux ne possède l'inférence de type (ou typage à la Hindley-Milner). Par exemple Anubis ne vous demande pas de déclarer le type de vos variables locales. Citation:
J'aimerais bien que l'on puisse reparler du langage et de son futur, plutôt que de s'acharner sur les détails d'une implémentation qui aura fait son temps. |
||
|
00
|
|
|
#190 |
|
Membre éclairé
![]() ![]() Inscription : août 2005 Messages : 417 ![]() |
Diverses remarques à propos de la surcharge.
Il y a en gros deux façons de traiter les collisions de noms de variables: la surcharge et la préemption. Si on admet la surcharge, les ambiguïtés doivent être résolues par inférence de types. Si on utilise la préemption (ce qui semble être le cas de Ocaml pour tous les symboles) on peut soit masquer la définition précédente, soit interdire de masquer. En ce qui concerne Anubis, j'ai choisi la surcharge pour les symboles globaux, et la préemption (sans interdiction de masquage) pour les symboles locaux. Ces derniers temps, on a eu parfois du mal à comprendre certains messages du compilateur, et on s'est aperçu qu'il y avait des masquages intempestif. Aussi, je pense que les symboles locaux en Anubis vont bientôt passer en préemption avec interdiction de masquer, pour éviter ce problème. Quelques remarques plus théoriques. Quand le compilateur Anubis interprète le symbole a dans l'exemple que j'ai donné, il trouve deux interprétations, l'une de type toto, l'autre de type bubu. En fait, il obtient une seule interprétation de a, mais cette interprétation est une méta-disjonction d'interprétations. De même, quand il interprète un symbole défini par un schéma, et qu'un paramètre de ce schéma n'est pas instancié, ce qu'il obtient est une interprétation méta-existentiellement quantifiée. Quand enfin, il ne trouve pas du tout d'interprétation (le symbol n'est pas défini), il obtient l'interprétation méta-faux. Les trois connecteurs logiques 'ou' (disjonction), 'il existe' et 'faux' (élément neutre de la disjonction) sont les seuls connecteurs additifs de la logique. Tous les autres sont multiplicatifs. L'inférence de types n'est en fait qu'une manipulation de ces trois connecteur additifs au niveau méta. je ne vois pas de raison d'adopter le quantificateur existentiel, et de renoncer à la disjonction. Autrement-dit, si on a du polymorphisme paramétrique, on doit aussi avoir la surcharge. C'est logique.
__________________
Ma page maths. |
|
|
00
|
|
|
#191 | ||
|
Membre Expert
![]() Inscription : mars 2002 Messages : 962 ![]() |
Voici par exemple un problème avec la surcharge de valeurs :
Code :
Est-ce qu'en mathématiques, deux identifiants de même nom peuvent exister, avec des valeurs différentes ? La surcharge de valeurs simples (par opposition aux fonctions) me semble dangereuse. D'ailleurs, on ne la voit dans presque aucun langage. |
||
|
|
00
|
|
|
#192 | ||||
|
Membre éclairé
![]() ![]() Inscription : août 2005 Messages : 417 ![]() |
J'ai compilé ton code sous la forme suivante:
Code :
Citation:
Citation:
__________________
Ma page maths. |
||||
|
|
00
|
|
|
#193 | |
|
Membre Expert
![]() Inscription : mars 2002 Messages : 962 ![]() |
Citation:
Mais, avoir des variables a, l'une qui vaut 2 et l'autre qui vaut 4.2, ça m'interpelle vraiment. Dans tous les langages que j'ai vus, la 2e valeur masque la première (cela génère parfois une erreur si les 2 valeurs ont la même portée). En revanche certains langages possèdent des conversions implicites. Si a est un entier qui vaut 2, il pourra être utilisé là où on attend un flottant (qui vaudra alors 2, lui aussi). En C++, on peut surcharger une fonction (en faisant varier sa signature), mais pas une valeur simple. En fait, dans la plupart des langages, la surcharge fonctionne uniquement sur les arguments. Le type de retour ne fait bien souvent pas partie de la signature. |
|
|
|
00
|
|
|
#194 |
|
Membre éclairé
![]() ![]() Inscription : août 2005 Messages : 417 ![]() |
Pour 0 c'est aussi le cas, et d'ailleurs j'ai souvent besoin de le rappeler aux étudiants. Par exemple, si E est un espace vectoriel sur le corps K, on a un 0 dans K et aussi un 0 dans E, qui sont distincts évidemment. Au début, pour les aider on met E ou K en indice de 0, mais rapidement on cesse de le faire.
Pour la version 1.8, j'ai commencé à expérimenter un système de conversions automatiques qu'on définit soi-même. Si on définit une conversion de Int32 vers Float, le premier exemple deviendra ambigu et ça ne passera plus. L'erreur sera même générée dès la deuxième définition de b. C'est peut-être là la solution du problème, car il semble bien venir de la possibilité de convertir (y compris mentalement et inconsciemment).
__________________
Ma page maths. |
|
|
00
|
|
|
#195 | ||||
|
Expert Confirmé Sénior
![]() ![]() |
Citation:
Citation:
Tu remarqueras d'ailleurs que tu autorise la surcharge d'opérateur, n'est-ce pas une façon moins contrôlé d'autoriser le même genre de chose (que je sache tu utilises le même opérateur d'addition pour tous tes types "numériques" ?). Citation:
dont le type serait inféré comme : Code Haskell :
(Ord a, Foldable f) => f a -> a Ainsi a doit être une instance de Ord, puisque max est utilisé sur a, et f doit être une instance de Foldable puisque j'utilise foldl1 (qui est un type de fold gauche ne fonctionnant que sur une structure non-vide (par contre là on aurait besoin des types dépendants pour le préciser, j'attend de voir Anubis2, pour l'instant Epigram est intéressant dans le domaine)). Cette fonction maximum fonctionnerait bien sûr sur une liste, mais également sur un tableau, ou un arbre, ou un type défini par le programmeur et pour lequel il a écrit une instance de Foldable. Citation:
-- Jedaï |
||||
|
|
00
|
|
|
#196 | |||||||||||||||||||
|
Membre Expert
![]() Inscription : mars 2002 Messages : 962 ![]() |
Citation:
Citation:
Citation:
Ca se rapporte aux contraintes sur les types. On peut par exemple définir la fonction f(a) où a est n'importe quel objet possédant les méthodes x, y, z. Citation:
Sans compter qu'encore une fois, la fonction ne marche que sur des listes. Calculer le maximum d'une liste ou d'un tableau (et on peut imaginer beaucoup d'autres structures de données de nature similaire) est sensiblement la même chose. Citation:
Citation:
La notion de type numérique est juste un regroupement de types. Par exemple, on pourrait dire que Int32, Int64, Float... font partie des types numériques. La fonction ci-dessus pourrait avoir pour type : Matrix($T) -> $T -> Matrix($T), à condition que $T soit un type numérique. Citation:
Citation:
Citation:
Avec Caml, il existe une option du compilateur pour générer le fichier interface. Avec F# et un bon éditeur, l'éditeur affiche les types sur simple demande. Je vais reprendre l'exemple d'Alex : Code :
Et quand on a le moindre doute sur un type, il suffit de le demander. Si tu n'es pas convaincu, je vais prendre un exemple un peu extrême. Pourquoi ne pas obliger les programmeurs à écrire "a@12" plutôt que "a" quand on veut utiliser la valeur "a" ? Ici, le 12 représente le numéro de la ligne où est défini. Cela enlèverait les ambigüités quand des valeurs sont redéfinies. Cela simplifierait la relecture : quand une fonction est appelée, on pourrait aussitôt où elle est définie. Malgré une lourdeur syntaxique au moment de l'écriture, cela pourrait à la relecture... non ? Je suis convaincu que ce n'est pas au programmeur de faire ça. Un bon éditeur devrait être capable de retrouver la définition d'une valeur sur un simple clic. Il devrait aussi être capable d'afficher le type d'une valeur sur simple demande. Code :
Mmh... Et le else correspondrait à quoi ? Si j'ai bien compris, cela fait ce que je veux... sauf qu'il n'y a pas de vérification statique. Si on passe un objet n'ayant pas plus ou times, l'erreur sera rencontrée à l'exécution. Citation:
Citation:
|
|||||||||||||||||||
|
|
00
|
|
|
#197 | ||||||||||||
|
Membre éclairé
![]() ![]() Inscription : août 2005 Messages : 417 ![]() |
Bon. J'ai fait une petite tentative d'imitation de la classe Ord d'Haskell. Tout ce qui suit compile sans problème.
La première définition a pour objet d'introduire la notation 'objet.membre' Code :
Code :
Code :
Maintenant voici les membres 'dépendants' de 'compare': Code :
Comme en Anubis il n'y a pas de classes de types, ma pseudo-classe est plutôt un type, en fait un schéma de type. En maths, on dirait que Ord est une structure, qu'une donnée de type Ord(X) (pour un certain type X) est une instance de cette structure, et que X est l'ensemble sous-jacent à cette instance. Maintenant, voyons l'utilisation de ma pseudo-classe (string_less est une primitive). Code :
Code :
En définitive, y a-t-il une différence vraiment profonde avec Haskell ? Je veux dire, y a-t-il quelque chose facilement faisable en Haskell et vraiment difficile, voire impossible à faire en Anubis ? En tout cas, il me semble que je vois une différence. En Haskell, la fonction max n'aura qu'un sens possible pour l'instance 'String', alors qu'en Anubis je peux fabriquer autant d'objets de type Ord(String) que je veux avec des fonctions de comparaison différentes.
__________________
Ma page maths. |
||||||||||||
|
|
00
|
|
|
#198 | ||||||
|
Membre éclairé
![]() ![]() Inscription : août 2005 Messages : 417 ![]() |
@LLB: Pour ce qui est de la généralité du 'map' tu as raison. C'est une chose à laquelle j'ai réfléchi ces derniers temps, et voici ce que j'ai décidé de faire pour Anubis 2.
En fait la correspondance $T ---> List($T) est un 'foncteur' au sens des catégories. C'est a dire que non seulement elle transforme un type en un type, mais elle transforme une fonction en fonction. En Anubis 2, on n'aura pas besoin de définir 'map'. Une fois que le schéma List sera défini, il suffira d'écrire: et ça donnera [f(1),f(2),f(3),f(4)]. De même si on définit le type d'arbre binaire suivant: Code :
pour que f (de type A -> B) soit appliquée à toutes les feuilles de l'arbre a (de type Tree(A)) , donnant un arbre de type Tree(B). Pour ce qui est de ta remarque sur la généricité, j'encourage à mettre des paramètres chaque fois que cela est possible. Je pense que c'est assez facile d'éduquer les programmeurs dans ce sens. Ceci dit il est vrai que le compilateur pourrait détecter le fait qu'un paramètre irait aussi bien qu'un type précis dans une définition et le signaler. C'est une idée qui me plait assez (je la trouve 'pédagogique'). Concernant l'exemple avec Matrix, regarde ce que j'ai fait dans mon post précédent avec 'Ord'. Je donnerai un autre exemple plus mathématique plus tard. Avec ton histoire de a@12, je crois que tu exagères un peu quand-même. Ceci dit, on a pour Anubis (sous Windows seulement malheureusement pour le,moment) l'IDE de Cédric Ricard qui fait le genre de choses que tu dis, et qui rend la programmation plus facile. Citation:
Citation:
__________________
Ma page maths. |
||||||
|
|
00
|
|
|
#199 | |
|
Membre éclairé
![]() ![]() Inscription : août 2005 Messages : 417 ![]() |
Citation:
anubis -index *.anubis et qui fabrique un joli fichier HTML plein de jolies couleurs avec toutes les definitions de types publics et toutes les déclarations de données publiques, y compris les constructeurs des types publics et les destructeurs implicites, et bien sûr des liens pour trouver les prototypes. Bizaremment, on s'en sert assez peu, mais ça existe.
__________________
Ma page maths. |
|
|
|
00
|
|
|
#200 | |||||||
|
Invité(e)
![]() Messages : n/a ![]() |
Citation:
Et qu'on ne viennent pas me dire que le langage n'a rien à voir avec l'implémentation, ou alors qu'on me file un lien vers la norme Anubis1. Un langage à implémentation unique (comme Anubis ou OCaml) est défini par son compilateur. Et dire qu'il ne faut plus parler de "cette implémentation qui a fait son temps" n'est pas fort sympathique pour les gens qui ont basé leur buisness sur cette implémentation. Pour revenir au "problème" de typage OCaml, qui a été bien expliqué après, je signale deux choses. La première est que l'on peut obtenir des messages d'erreurs bien plus ludique tel que : Code :
Code :
Code :
|
|||||||
00
|
Copyright © 2000-2013 - www.developpez.com