Guillaum a écrit 472 commentaires

  • [^] # Re: Lisibilité du code

    Posté par  (site web personnel) . En réponse au journal Switch, chaîne constante et c++. Évalué à 4.

    Question d'habitude. Quand tu commence à jouer avec des API fluent, ça se lit vite.

    En fait l’implémentation est complexe, mais l'usage est relativement simple et lisible. De mon point de vue il n'est pas forcement nécessaire de comprendre l’implémentation d'une abstraction pour l'utiliser correctement.

    Ainsi j'aime bien l'approche StringSwitch qui est très claire (à mon sens). Je trouve que l'approche à base de std::map ne devrait pas exposer la std::map directement ni le find. Je ferais un truc du genre : (Api non testé).

    template<typename Result, typename Value>
    Result Switch(Value v, std::map<Value, std::function<Result()>>);
  • [^] # Re: Pas uniquement string

    Posté par  (site web personnel) . En réponse au journal Switch, chaîne constante et c++. Évalué à 2.

    Question de goût. Je trouve la syntaxe au contraire très clair pour du code templaté.

    La dessus, rien à dire ;) Mais bon, j'ai un peu pris l'habitude d'Haskell (ou autre). Pour le troll, comparons :

    int fact(int n)
    {
        var<int> m;
    
        Match(n)
        {
          When(0)     return 1;
          When(1)     return 1;
          When(_)     return n * fact(n-1);
        }
        EndMatch
    }
    fact n = case n of
       0 -> 1
       1 -> 1
       _ -> n * fact (n - 1)
    
    -- ou
    
    fact 0 = 1
    fact 1 = 1
    fact n = n * fact (n - 1)

    Vachement plus compact, lisible et polymorphique ;) (mais bon, je troll ;)

  • [^] # Re: Pas uniquement string

    Posté par  (site web personnel) . En réponse au journal Switch, chaîne constante et c++. Évalué à 2.

    Je m'auto répond. Un point qui sent vraiment mauvais dans cette approche c'est que pour stocker le résultat d'un match, il faut déclarer une variable non initialisée, exemple :

    int fib2(int n)
    {
        var<int> m;
    
        Match(n)
        {
          When(1)     return 1;
          When(2)     return 1;
          When(2*m)   return sqr(fib2(m+1)) - sqr(fib2(m-1));
          When(2*m+1) return sqr(fib2(m+1)) + sqr(fib2(m));
        }
        EndMatch
    }

    Ce qui veut dire que on peut se servir d'une variable mal initialisée dans une branche. J'ai tendance à ne plus supporter çà et à refuser les revues de code qui en contiennent.

  • [^] # Re: Pas uniquement string

    Posté par  (site web personnel) . En réponse au journal Switch, chaîne constante et c++. Évalué à 2.

    C'est pas clair à quel point c'est du C++ ou à quel point c'est une extension au standard actuel. La syntaxe est tout de même vraiment répugnante.

    Avec d'autres langages, le pattern matching c'est extrêmement puissant, concis et clair et cela fait partie des outils qu'il me manque dramatiquement quand je fais du C++.

  • # Arguments ?

    Posté par  (site web personnel) . En réponse au journal Java (EE) Sapu cépalibre.. Évalué à 10.

    Ne me parlez pas de trucs à base de Python ou pire à base de PHP SVP, c'est pas sérieux

    Peux-tu développer ?

    Note : bien que j'imagine que ma question soit surement hautement sujette à troll, ce n'est pas mon but. Je ne connais absolument pas l'écosystème Java et donc je ne peux pas vraiment comprendre pourquoi ce n'est pas sérieux. C'est juste le débat statique / dynamique ou c'est autre chose ?

  • [^] # Re: Grammaire en boucle infinie ?

    Posté par  (site web personnel) . En réponse au journal Génération de code (Python) avec Grako. Évalué à 4.

    Merci.

  • [^] # Re: Grammaire en boucle infinie ?

    Posté par  (site web personnel) . En réponse au journal Génération de code (Python) avec Grako. Évalué à 4.

    multiplication = (expression | terme) "*" (terme | expression) ; donc la première chose qu'il test c'est (expression | terme) avant de tester "*", et donc ma peur de la récursion.

    J'ai raté une étape ?

  • # Grammaire en boucle infinie ?

    Posté par  (site web personnel) . En réponse au journal Génération de code (Python) avec Grako. Évalué à 4.

    Comment la grammaire peut ne pas donner une boucle infinie ?

    Si tu veux parser 45 avec expression, il vas échouer sur le test de la présence de ( et donc vas passer dans contenu_expression puis dans multiplication puis dans expression et ainsi de suite.

  • [^] # Re: Proposition de calcul de la valeur que compte un vote selon la durée de vie restante du votant

    Posté par  (site web personnel) . En réponse au journal Démocratie et pyramide des âges. Évalué à 6.

    Les vrais programmeurs n'utilisent pas d'affectation mais seulement des équations en programmation fonctionnelle pure ;)

  • [^] # Re: Unicode et Haskell

    Posté par  (site web personnel) . En réponse à la dépêche Le compilateur GHC Haskell en version 8.0.1. Évalué à 3.

    Merci pour ces précisions. J’avoue que je me suis permis ces raccourcis car la dépêche (initialement un journal écrit entre midi et deux pendant un moment de procrastination) était déjà fort long. Comme je le disais c'est un exemple perfectible que je ne voulais pas surcharger ;)

    Notez que les opérateurs en Haskell sont des fonctions comme les autres (en dehors de leur syntaxe) et peuvent être définis dans n'importe quel module. C'est une force qui peut parfois être légèrement abusée au goût de certains… ;-)

    (lens mon amour…)

    Je me permet d'ajouter, pour tout ceux qui seraient tentés par Haskell, au début vous aller croiser quelques opérateurs dont quatre en particulier : le couple <$> et <*> ainsi que le couple >> et >>=. Ces opérateurs font initialement peur, mais il peuvent se comprendre sur des cas très simples et après s'appliquer dans de nombreux cas génériques et deviennent aussi naturel que le ; et le for peut l'être en C.

  • [^] # Re: Merci pour cette annonce

    Posté par  (site web personnel) . En réponse à la dépêche Le compilateur GHC Haskell en version 8.0.1. Évalué à 3.

    Je comprend ta réponse.

    C'est juste que je ne vois pas vraiment cela comme un problème. Au final on serait en train de reprocher à GHC de faire avancer un langage au détriment d'un standard qui stagne. Il n'y aurait pas eu de standard et une unique implémentation, comme c'est le cas pour beaucoup de langages que tu cites, cela n'aurait pas posé de problème.

  • [^] # Re: Point de vue de novice

    Posté par  (site web personnel) . En réponse à la dépêche Le compilateur GHC Haskell en version 8.0.1. Évalué à 8.

    il faut y passer beaucoup de temps, un apprentissage sérieux prendra plus de six mois.

    Un outil complexe demande forcement un apprentissage pour maitriser, mais tu peux commencer à écrire du code qui marche assez rapidement. J'ai écris un lancer de rayon dans ma première semaine d'Haskell, alors que je n'avais encore rien compris au langage (Et j'ai du tout réécrire après ;)

    il manque les déclarations de type. Celles ci ne sont pas obligatoires en Haskell mais vivement conseillées car elles permettent de mieux comprendre le code et évitent aussi les erreurs lors de la rédaction d'un programme.

    L'absence d'annotation de type est un choix de ma part pour montrer que le coté typage statique du langage n'impose pas forcement une syntaxe verbeuse. Cet exemple permet de montrer un "vrai" problème qui mixe des entrées-sorties, des structures de donnée autre que la liste et de la composition de fonctions. Internet est plein d'introduction à Haskell à base de Fibonnaci, de quick-sort (bugés) et de factorielle, mais les exemples montrent rarement d'entrées-sorties ce qui laisse sur cette fausse impression que c'est compliqué.

  • [^] # Re: Sympa la référence à docteur Folamour

    Posté par  (site web personnel) . En réponse à la dépêche Le compilateur GHC Haskell en version 8.0.1. Évalué à 2.

    Bonne séance ;)

  • [^] # Re: Merci pour cette annonce

    Posté par  (site web personnel) . En réponse à la dépêche Le compilateur GHC Haskell en version 8.0.1. Évalué à 2.

    Ce qui est un des points qui me gêne chez Haskell (mais j’aime quand même le langage par ailleurs).
    Un standard qui est là seulement pour faire joli c’est déjà moyen, mais en plus le fait qu’une grosse majorité du code qui dépends d’extension GHC rend très difficile (voir impossible) l’apparition d’un compilateur alternatif.

    Je ne connais aucune technologie qui actuellement garantie la portabilité entre différents compilateurs / système / … En C++ le standard n'est pas forcement respecté et avoir du code portable entre les différents compilateurs n'est pas si triviale que ça. En Python, il n'y a pas de standard et il existe plusieurs implémentation différentes.

    Alors si demain quelqu'un veut faire un nouveau compilateur Haskell, il lui faudra une sacrée bonne raison et si elle est suffisamment valable, je suis près à limiter mon usage de certains extensions pour assurer la portabilité.

    On pourrait aussi se dire que le standard Haskell pourrait évoluer pour intégrer ces extensions, mais au final cela ne ferait que repousser le problème qui est qu'il devient difficile avec toute technologie complexe de repartir de zéro.

  • # Autres changements interessants

    Posté par  (site web personnel) . En réponse à la dépêche Le compilateur GHC Haskell en version 8.0.1. Évalué à 10.

    Je vais compléter ma propre dépêche de nouveaux trucs intéressants que je n'avais pas encore noté ou réalisé l’intérêt.

    Amélioration du Shell ghci.

    Trois petites améliorations simple du shell :

    • Les erreurs sont maintenant listées avec de beaux caractères unicode avancés pour faire les points d'une liste.
    • Il est maintenant possible de définir dans le shell des fonctions et des noms sans devoir utiliser let. Au lieu de taper let x = 5, on peut taper x = 5.
    • Dernier point que j'apprécie particulièrement. En haskell, une erreur de type est critique et fait planter la compilation. C'est énervant lors d'un refactoring car on ne peut pas lancer ses tests unitaires tant que tout n'est pas fixé. Heureusement GHC permet de compiler avec -fdefer-type-errors qui transforme les erreurs de type en erreur au runtime. Maintenant le shell permet de charger un module normalement avec :load ou en reportant les erreurs au runtime, avec :load!.

    Extensions XTypeApplications

    Cette extension permet de spécialiser lors de l'appel une fonction polymorphique.

    Prenons par exemple la paire de fonction encode et decode de Data.Serialize (package cereal) présentée rapidement dans la dépêche.

    > :type decode
    decode :: Serialize a => ByteString -> Either String a
    > :type encode
    encode :: Serialize a => a -> ByteString

    La fonction encode prend n'importe quel type a et retourne une ByteString, bref elle serialize. La fonction decode réalise l'opération inverse, prenant une ByteString et retournant un Either String a, c'est à dire soit une erreur sous forme de String, soit un type a.

    Le problème c'est que encoder est simple, le type a est connu puisque on le passe directement en paramètre, ici avec une liste de booléens [Bool] :

    > encode [True, False, True]
    "\NUL\NUL\NUL\NUL\NUL\NUL\NUL\ETX\SOH\NUL\SOH"

    Pour le décodage c'est plus complexe, puisque on ne connait pas le type de sortie :

    > :type decode (encode [True, False, True])
    decode (encode True) :: Serialize a => Either String a

    Très souvent le moteur d'inférence s'en sort car ce type va être utilisé directement après, par exemple :

    > (decode (encode [True, False, True])) == Right [True, False, True]
    True

    Ici on compare le contenu décodé à Right [True, False, True], ce qui veut dire qu'il a bien réussis à décoder. En écrivant cette comparaison, le type à décoder ne peut qu'être un Bool.

    Mais si on se sert après d'une fonction polymorphique pour lequel le type peut être ambigu, par exemple la fonction length qui accepte tout type de conteneur, alors il faut spécifier le type.

    Plusieurs solutions s'offrait à nous avant :

    • spécifier le type de la fonction decode. Rappel, celui-ci est ByteString -> Either String a et il faut forcer a, ce qui donne une annotation de type :: ByteString -> Either String [Bool]
    • spécifier le type de la valeur de sortie. Rappel, celui-ci est Either String a, il faut donc forcer a, ce qui donne :: Either String [Bool].

    Dans ce cas simple c'est supportable, mais dans le cas d'un type bien plus complexe, il est rébarbatif de recopier tous les types déjà connus (ici ByteString et Either String) pour ne specifier qu'un petit type polymorphique. GHC 8.0 permet de spécifier spetialement les types polymorphique d'une fonction gràce à @. Dans le cas de decode il n'y a qu'un seul type à spécifier et cela se fait donc de la façon suivante : decode @[Bool].

    Comparez les trois approches :

    (decode :: ByteString -> Either String [Bool]) (encode [True, False, True)
    (decode (encode [True, False, True])) :: Either String [Bool]
    decode @[Bool] (encode [True, False, True])

    Plus compact et plus clair.

  • [^] # Re: Erreur dernier lien

    Posté par  (site web personnel) . En réponse à la dépêche Le compilateur GHC Haskell en version 8.0.1. Évalué à 2.

    Merci. J’espère que quelqu'un fera l'édition.

    Ce lien est d'ailleurs un peu dur à suivre et j'aurais dû recommander le tutoriel liquid haskell à la place.

  • [^] # Re: typage statique automatique ?

    Posté par  (site web personnel) . En réponse au journal Typage statique pour Python. Évalué à 2. Dernière modification le 01 juin 2016 à 18:09.

    Tu as la chance de faire du haskell professionnellement, j'aimerais bien ;)

    De mon expérience amateur, je met des types sur les "top level", mais c'est tout. Il y a deux-trois cas tordus, quand tu commences à abuser de typeclass ou OverloadedLists / OverloadedStrings, mais en pratique cela ne m'a jamais trop embeté.

    Mais il est clair que ma remarque était en comparaison avec par exemple C++ où tu met des types partout.

    (Edit: la raison qui fait que ce problème existe moins en OCaml est surement du justement à l'absence de typeclass, donc l'absence des ambiguïtés qui peuvent en résulter).

  • [^] # Re: On s'en bat le steak

    Posté par  (site web personnel) . En réponse au journal Typage statique pour Python. Évalué à 2.

    C'est maintenant rarissime que j'obtienne des erreurs de type au lancement.

    Rarissime c'est trop ;) De mon coté, je suis moins chanceux / doué et je ne compte plus le nombre de fois où je lance un code python qui se vautre immédiatement sur une TypeError, où un mauvais nombre d'argument à une fonction, où une typo dans le nom d'une fonction ou même une erreur d'imbrication (Je pensais que j'avais un object mais en fait je manipulais une liste d'objets). Et le pire c'est quand cela ne se vautre pas immédiatement ;)

    Pour ne pas me contenter d'un troll, quel type d'erreurs obtient-tu au lancement qui ne sont pas des erreurs de type ? Plus le typage est avancé, plus de nombreuses erreurs de "logique" peuvent être captées par le système de type lors de la compilation, enfin du moins c'est l’expérience que j'ai.

  • [^] # Re: C++, où quand le code asm généré est plus lisible

    Posté par  (site web personnel) . En réponse au journal [C++14 ] Expressions template pour les nuls. Évalué à 2.

    D'autres langages vont proposer soit de résoudre le problème au runtime en créant l'arbre d'opération à l’exécution et en générant du code avec un compilateur intégré (JIT), c'est ce que font pas mal d'approches au dessus de numpy, numba, … Tu peux aussi générer du code OpenCL à la volée. Ces approches ont l'avantage de pouvoir en plus avoir une vision sur la taille de la structure, ses caractéristiques et la machine utilisée (Quantité de ram, taille des caches, …), et ainsi pouvoir générer un code plus adapté. En contrepartie c'est encore plus complexe et cela a un coût lors de l’exécution ;)

    D'autres langages vont proposer un outil de méta-programmation lors de la compilation qui est "plus lisible" que les templates C++.

    D'autres langages ont le support de ce type d'optimisation en natif dans le compilateur (regarde Haskell, les fusion et les rewrite rules), mais à ma connaissance cela ne donne pas ce niveau de satisfaction.

    Au final on est dans un domaine de la performance à tout prix et c'est dur, cela fait du code crade et seulement maintenable par des gars qui ont 3 PhD, dont un consacré à la maintenance du code en question, mais bon, c'est ça qui est marrant non ? (Il faut voir que l'exemple donné dans ce journal est "trivial" et "très lisible"… si si… ;(

  • [^] # Re: Avantage ?

    Posté par  (site web personnel) . En réponse au journal [C++14 ] Expressions template pour les nuls. Évalué à 7.

    Dans ton exemple c'est évident, mais imagine maintenant un pipeline avec une centaines d'opérations de haut niveau elles même définies par une dizaine d'opérations de bas niveau, tout de suite c'est moins facile à inliner à la main, regarde cet exemple :

    auto preprocess = [](const auto &im){ return malib::gammaCorrect(malib::tonemap(malib::greyscale(im))); };
    
    auto image = malib::max(malib::absdiff(preprocess(im0), preprocess(im1)));

    Un autre example intéressant, imagine que tu ai un pipeline complex et que tu ne veuilles qu'une sous partie de l'image :

    
    auto image = malib::regionOfInterest(rectangle, pipelineComplex(im0, im1, im2, im3, im4);
    

    Tu ne veux pas calculer toute l'image alors que le regionOfInterest limite le calcul à une petite sous partie. Cette technique peut te permettre de compiler un code qui n'effectue le traitement que sur sa sous partie SANS copier une seule image. Note que tu peux aussi spécialiser les templates pour générer du code vectorisé efficace ou des accès mémoire plus sympathiques. (Remarque, ce problème ne se simplifie pas en distribuant regionOfInterest sur les images d'entrée car ton pipelineComplex peut effectuer un traitement global)

  • [^] # Re: On s'en bat le steak

    Posté par  (site web personnel) . En réponse au journal Typage statique pour Python. Évalué à 3.

    Là où Haskell peut faire peur, c'est que comme il fait du fonctionnel pur il faut recourir à des monades pour les effets de bords : ça peut effrayer au premier abord.

    C'est ce qui m'a séduit dans Haskell.

    OCaml de son côté permet de mélanger les paradigmes impératif, fonctionnel et objet; et faire de la réutilisation de code (comme pour le paradigme objet) via les variants polymorphes (je ne sais pas s'il y a un équivalent en Haskell).

    Non, pas l'impression que ce soit possible en effet.

  • [^] # Re: On s'en bat le steak

    Posté par  (site web personnel) . En réponse au journal Typage statique pour Python. Évalué à 7.

    Je pensais comme cela il y a quelques temps. J'ai fais 7 ans de python acharné sur des projets libres ou sur mes projets persos et j'étais convaincu qu'une bonne couverture de test était la solution à tous les problèmes. J'ai fais des présentation en entreprise où à l'université à ce sujet. J'ai appliqué cela pendant 6 ans en entreprise. Puis un jour j'ai découvert que la couverture de test à 100 % n'existait pas, que souvent du code n'avait pas été documenté parce que pas le temps et que de toute façon, même avec des tests et de la documentation, le refactoring en python me péterait toujours à la gueule en production dans le petit bout de code bien tordu qui appelait la fonction que j'avais raté. C'est l'example donné dans la présentation sur mypy de ce journal, quand tu as une method dosomething sur un objet et que un grep dans ta base de code te donne 150 instances de dosomething, lequelles tu dois modifier ?

    Puis j'ai découverts ce qu'apporte le typage statique, en haskell principalement (mais j'ai fais un peu de rust et de ocaml). Quand tu refactor tu as une quantité d'erreur que tu ne peux plus faire du fait du typage statique. Un bon système de type évite aussi des erreurs immonde qui peuvent arriver avec un mauvais système de type (comme des casts implicites entre des matrices et des booléens, histoire vécue qui m'a fait perdre 10% de performance pendant 6 mois sur un logiciel "haute performance"). Un bon système de type évite aussi les null pointer exception. Un bon système de type te permet de modéliser avec les types et une fois que tu as trouvé les bonnes abstractions, tu peux écrire du code très rapidement. Et un bon système de type ne râle que quand tu fais des bêtises.

    Après, un mauvais système de type, c'est juste lourd et contraignant, cela râle tout le temps pour rien, et malheureusement on se tape beaucoup de mauvais système de type comme example de ce qu'est le typage. Si votre idée d'un système de type est celle présentée dans Java ou C++, je vous invite vraiment à vous refaire une idée en allant voir ce qui se fait à coté. Rust est un bon point d'entrée, Haskell est énorme mais fait peur. Ocaml c'est sympa.

    Bonne découverte.

  • [^] # Re: A force...

    Posté par  (site web personnel) . En réponse au journal Typage statique pour Python. Évalué à 4.

    Le système d'inférence ne fait pas d'hypothèse, seulement des observations et une propagation de contraintes.

    Dans le cas de Haskell, si tu écris une fonction pgcd, supposons comme ceci :

    gcd' a b
      | b > 0 = gcd' b (mod a b) -- cas 1
      | otherwise = a            -- cas 2

    (Note, je l'appelle gcd' car gcd est déjà pris par une fonction qui fait la même chose… ;)

    Le type se déduit de façon suivante :

    • il faut pouvoir appliquer (>) sur b et (>) est définie dans la typeclass Ord
    • il faut pouvoir appliquer mod a b, le type de mod est Integral t => t -> t -> t, donc on conclue que a et b sont dans la type classe Integral.
    • Integral étant plus restrictif que Ord, alors la seule contrainte c'est que a et b soient de type Integral
    • le type de retour de gcd' est soit le même que gcd' (cas 1), soit le même que a (cas 2).

    On déduit donc le type suivant : gcd' :: Integral t => t -> t -> t.

    À partir du moment où tu passes n'importe quel type qui implement Integral, alors tu n'auras pas de soucis. Si tu passes un autre type, cela ne marchera pas, mais bon, c'est normal, ton type ne sait pas faire…

    Le seul problème ici c'est que, au sens de pas mal de monde, Integral est trop restrictif, puisque que pour implementer Integral il faut implement Integral et toutes ses sous typeclass, dont Real, Enum, Num, Ord et Eq avec, au minimum :

    • Integral : quotRem (div et mod en gros), toInteger
    • Real : toRational
    • Enum : toEnum, fromEnum
    • Num : (+), (*), abs, signum, fromInteger, negate ou (-)
    • Ord : (<=)
    • Eq : (==) ou (/=)

    Ce qui fait beaucoup de bordel à implementer pour calculer un pgcd sur un type custom. La hiérarchie de type pour les nombres en Haskell est trop restrictive à mon gout, mais bon, personne n'est parfait ;) (En pratique cela ne pose rarement de problème sauf quand tu es un fou d'architecture par les types, mais à ce moment là Haskell n'est plus assez puissant pour tes besoins…)

    De plus, il n'est pas obligatoire mais fortement conseillé de suivre des règles lors de l'implementation de ces classes (genre s'assurer que a + b == a - (negate b)).

    Cependant, contrairement à de nombreux langage, ce mécanisme impose une certaine cohérence. Quand tu vois a / b, tu sais que c'est l’opérateur / de la classe Fractional et tu peux t'attendre a un comportement. Ce n'est pas le cas en python ou C++ ou le / peut être surchargé en n'importe quoi. Par example, certains s'en servent pour concaténer des chemins de fichiers. C'est une restriction mais j'ai appris à l'aimer car cela rend le code très explicite.

  • [^] # Re: typage statique automatique ?

    Posté par  (site web personnel) . En réponse au journal Typage statique pour Python. Évalué à 4.

    Je suis un peu déçu, je pensais que c'était inféré.

    Il y a méprise. Ce qui est inferé c'est le type ou la classe (i.e. "interface" nécessaire d'une fonction). Mais par contre il faut bien dire ce que fait ton interface pour un type particulié. Par example si j'ai une interface Surface avec une fonction surface et que j'ai un type Rectangle, il faut bien que j'écrive quelque part la fonction surface du Rectangle… Par contre quand je vais écrire une fonction qui utilise surface, son type sera inféré comme prenant un type d'interface Surface en paramètre.

    Oui je me suis mal exprimé, ce que je voulais dire c'est qu'une méthode prend un et un seul type par paramètre et que si on cherche à la rendre polymorphique, il faut définir un type somme.

    Oui et non.

    Les types sommes sont une manière d'encapsuler plusieurs trucs dans un seul type, c'est grosso modo une union typé.

    Tu peux aussi définir ta fonction dans une typeclass, c'est à dire qu'elle prendra un type qui match la typeclass (i.e. l'interface). Elle est donc plus ou moins polymorphique.

    Elle peut aussi être polymorphique car on se fout des types que du passes. Example d'une fonction swap (a, b) = (b, a), celle-ci prend un tuple de type (t, t') et retourne un tuple de type (t', t), en ce sens elle est polymorphique.

  • [^] # Re: typage statique automatique ?

    Posté par  (site web personnel) . En réponse au journal Typage statique pour Python. Évalué à 3.

    -- Un exemple de fonction qui est polymorphique
    afficherListe :: (Affichable a) => [a] -> String
    afficherListe = intercalate "," . map affiche

    Petite remarque pour ceux qui ne lisent pas l'haskell couramment. La fonction afficherListe prend une liste de a et renvoie une String. a marche pour n'importe quel Affichable, mais il faut bien noter que la liste ne peut contenir qu'un seul type et le même.

    En gros, si on a foo et bar des valeurs de type Foo et Bar, eux même instances de Affichable, on peut faire afficheListe [foo, foo, foo] ou afficherListe [bar, bar, bar] mais pas afficherListe [foo, bar].

    En gros ce n'est pas l'équivalent des appels virtuels en C++ ou de l'héritage en python, tout est résolu à la compilation et une instance spécifique de afficherListe est générée pour chaque a possible. (Ce n'est pas complètement vrai ;)

    La notion de polymorphisme à l'execution (i.e. héritage virtuel) est plus complexe en haskell…