Concours de logo Haskell

Posté par . Modéré par j.
Tags : aucun
7
18
déc.
2008
Communauté
Le site haskell.org et plus généralement la communauté Haskell a lancé un concours pour le renouvellement de leur logo. L'actuel est un peu vieillot et ne reflète pas complètement la maturité du langage.

Le principe est donc de proposer un logo plus moderne devant représenter la simplicité du langage. Pour cela il suffit de s'inscrire sur le wiki et de le poster en indiquant son nom. Les créations doivent être de taille correcte (pas trop grand), sur fond blanc de préférence et sous une licence permettant la libre distribution.

La limite du concours est le 31 décembre 2008.

Pour rappel, Haskell est un langage de programmation fonctionnel créé en 1985. L'un des programmes les plus répandu écrit en Haskell est probablement le gestionnaire de versions Darcs.

NdM: Merci à Ummon pour son journal sur le sujet.
  • # Haha.

    Posté par . Évalué à 3.

    Le principe est donc de proposer un logo plus moderne devant représenter la simplicité du langage.

    Moi j'aime bien l'actuel*, au moins il a le mérite de rappeller les caractéristiques du langage : le « :: » pour le typage, la flêche « -> » pour la programmation fonctionnelle, le « ∀ » pour le polymorphisme, l'implication « => » pour la logique et enfin le « >> » pour les monades (et plus globalement la théorie des catégories).

    Cela dit il faut reconnaître que c'est assez lourd symboliquement, et que ça peut (?) repousser les débutants.

    * : http://www.willamette.edu/~fruehr/logos/PNGs/HaskellLogo.png
    • [^] # La simplicité d'Haskell????

      Posté par . Évalué à 2.

      J'ai cru que c'était un troll de l'auteur de l'article mais non c'est vraiment dans l'intitulé du concours..

      Pour illustrer la "simplicité" d'Haskell je propose cela:
      http://blog.shikiryu.com/wp-content/uploads/2007/06/pendu.jp(...)

      PS:
      Si vous considerer qu'Haskell est simple et que je trolle alors vous devez être capable de m'expliquer ce qu'est un monade *simplement*, après tout c'est utilisé pour faire des entrée/sortie en Haskell ce qui est plutôt fondamental comme besoin!
      • [^] # Re: La simplicité d'Haskell????

        Posté par . Évalué à 10.

        Une monade (T, eta, mu) sur une catégorie C est formée d'un foncteur T de C dans C et de deux transformations naturelles : eta de l'identité sur C vers T et mu de TT vers T, satisfaisant certaines équations.

        (Haskell en dix secondes : expr :: type indique que l'expression expr est de type type; \id -> expr déclare une nouvelle fonction anonyme prenant en paramètre une variable id et ayant pour corps expr. Par exemple,

        ajoute2 x = x + 2

        est équivalent à \x.x + 2.)

        Haskell, est un langage purement fonctionnel, ce qui signifie que ses fonctions ne font pas d'effets de bords : appliquées au même argument, elles rendent toujours le même résultat, comme en mathématiques. Évidemment, on ne peut parfois pas échapper aux effets de bords : par exemple, pour faire des entrées sorties !

        Imaginons, par exemple, une fonction readLine qui lit une chaîne de caractères sur la console. En Haskell, chaque programme se voit attribuer un type unique par le compilateur*; imaginons celui de notre fonction readLine. Premier jet :

        readLine :: QQchose -> String

        Qu'est-ce que ce QQchose ? A priori, rien de bien particulier. En C, on déclarerait une fonction sans paramètres (pour les dinos, void); en Haskell ça n'a pas de sens. On dispose justement d'un type approprié : () (à prononcer « younite »), habité par une unique valeur, elle aussi notée (), ce qui peut d'ailleurs entretenir une certaine confusion.

        On a donc :

        readLine :: () -> String

        Problème plus grave : ça ne correspond pas à ce que j'ai décrit plus haut. Il est évident qu'un appel readLine () renverra différentes valeurs selon ce que rentre l'utilisateur... Intuitivement, le coeur du problème est que readLine, en faisant un effet de bord, agit sur le environnement qui l'entoure. On pourrait modéliser cela par le fait qu'on dispose d'un type Envir symbolisant ce fameux monde/environnement; son type deviendrait alors :

        readLine :: Envir -> String

        Se pose un nouveau problème : de toute évidence, readLine a... changé le monde ! Plus précisément, elle a entre autres consommé l'entrée de l'utilisateur sur le terminal où est lancé notre application. Elle doit donc nous renvoyer le nouveau monde obtenu après qu'elle ait effectué ses opérations.

        readLine :: Envir -> (String, Envir)

        Bien, il semblerait que nous ayons progressé dans notre modélisation. Maintenant, imaginons par exemple une fonction putLine qui affiche une chaîne sur la console puis passe à la ligne (un équivalent du puts de C). Nous savons que le type :

        putLine :: String -> ()

        Ne fonctionnera pas; notre fonction faisant des effets de bords, on va imiter la méthode utilisée ci-dessus et prendre un argument supplémentaire, sous forme du monde, et renvoyer le nouveau monde obtenu :

        putLine :: String -> Envir -> ((), Envir)

        Maintenant, nous pouvons écrire du code de ce style :


        repete w =
        let (ligne, w') = readLine w in
        putLine ("Vous avez entre " ++ ligne) w'


        Petit exercice : quel sera le type de notre fonction demandeAge ?

        demandeAge :: Envir -> ((), Envir)

        Ça fonctionne. Toutefois, a priori il reste un problème : je peux me tromper en passant mes « mondes » d'appel en appel, ici par exemple en confondant w et w' dans l'appel à putLine. En fait, idéalement, la « plomberie » de passage du « monde » ne devrait pas être réalisée explicitement par le programmeur.

        Pour résoudre ce problème, on peut commencer par remarquer l'apparition progressive d'un motif récurrent dans nos types :

        Envir -> (truc, Envir)

        Étant programmeurs, nous sommes feignants : et si on s'abstrayait de ce type en l'encapsulant dans une nouvelle abbréviation ? On va déclarer un nouveau type, synonyme du précédent** :

        type IO truc = Envir -> (truc, Envir)

        Et là, on vient mine de rien de réaliser une petite percée conceptuelle : on est désormais capable de distinguer, en regardant son type, un programme faisant potentiellement des effets de bords d'un programme n'en faisant pas. Nos types deviennent :

        readLine :: IO String
        putLine :: String -> IO ()
        demandeAge :: IO ()

        Maintenant, il est intéressant de se demander quelles fonctions génériques sont communes à tous nos « programmes à effets de bords potentiels ». Déjà, on peut voir tout programme comme faisant potentiellement des effets de bords, en remarquant qu'il suffit qu'il ne modifie pas le monde qu'il reçoit :

        return :: a -> IO a
        return a = \w.(a, w)

        Ensuite, on aimerait, comme dit précédemment, éliminer la plomberie, c'est-à-dire le passage explicite du monde d'une fonction à l'autre. Essayons de nos laissons guider par, par exemple, la composition des fonctions readLine et putLine. Imaginons une hypothétique fonction compose portant bien son nom; quel serait son type ?

        compose :: (IO String) -> (String -> IO ()) -> IO ()

        Soit, en s'abstrayant des types « concrets » String et () :

        compose :: IO truc -> (truc -> IO bidule) -> IO bidule

        Cette fonction est traditionnellement notée « >>= », à prononcer « bind ». Notre code devient donc :

        demandeAge :: IO ()
        demandeAge = readLine >>= (\ligne.putLine ("Vous avez entré " ++ ligne))

        Qu'avons nous gagné ? Et bien, si on cache a l'utilisateur la nature de notre type IO, par exemple dans une bibliothèque séparée, il n'est plus possible de se tromper dans le passage des mondes. Il deviendra également impossible de mélanger arbirairement code « pur » et code à effets de bords. Cerise sur le gatal, Haskell propose du sucre syntaxique automatique se traduisant simplement dans le style utilisé ci-dessus, mais qui permet d'écrire :

        demandeAge :: IO ()
        demangeAge = do {
        ligne <- readLine;
        putLine ("Vous avez entré " ++ ligne);
        }

        Comme vous devez déjà vous en douter depuis un petit moment, ce triplet (IO, return, >>=) est... une monade. Et il s'avère que ce genre de construction apparaît TRÈS souvent en programmation, grosso-modo dès qu'on a besoin de parler d'un calcul se faisant dans un certain contexte; citons par exemple la monade des listes qui nous permet de programmer de façon non-déterministe. Elles viennent de la théorie des catégories, une jolie branche des mathématiques abstraites qui sert de plus en plus en théorie (et pratique :-) ?) des langages de programmation.

        Donc, pour la définition programmatique : une monade est un triplet (T, return :: a -> T a, (>>=) : T a -> (a -> T b) -> T b), satisfaisant six équations que je vous épargnerais, mais qui, intuitivement, impliquent que return et >>= « coopèrent » bien.

        Une monade est donc une façon simple de représenter et encapsuler des programmes s'exécutant dans un certain contexte, de les distinguer par leurs types, et de les composer. Pour plus de détails, je suggère la lecture de l'excellent papier de Philip Wadler : http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/ba(...)

        ...

        Bon, ok, le Haskell n'est pas simple; mais au moins, il est différent, et je pense qu'il vaut la peine d'être appris, à défaut d'être utilisé.

        * : Je simplifie un peu.

        ** : Je prends mes aise avec les déclarations de type Haskell pour la clareté de l'exposé, que les puristes m'épargnent.
        • [^] # Re: La simplicité d'Haskell????

          Posté par . Évalué à 1.

          Très belle explication!

          Je m'amuse depuis pratiquement un an avec Haskell maintenant.
          Et c'est seulement depuis un ou deux mois que j'ai eu le déclic, et que je peux vraiment dire que c'est un langage expressif pour moi. Avant cela, il m'était impossible d'écrire du code qui fasse vraiment quelque chose, et très difficile de lire le code des autres.

          Pour moi c'est un peu comme apprendre une nouvelle langue, qui au départ parait totalement hermétique (genre le Mandarin, ou le Japonais)

          D'abord c'est extrêmement gratifiant à chaque fois qu'on comprend un truc nouveau, et cela ouvre vers une autre manière de penser les choses.
      • [^] # Re: La simplicité d'Haskell????

        Posté par . Évalué à 3.

        Bon je ne resiste pas plus longtemps a la tentation de me faire moinsser :

        http://www.xent.com/pipermail/fork/Week-of-Mon-20070219/0441(...)

        :-)
        • [^] # Re: La simplicité d'Haskell????

          Posté par . Évalué à 2.

          Un pote qui apprenait le Ruby m'avait envoyé un petit bout de code¹ qu'il avait écrit.

          Le but était de calculer les probabilités de réussite pour le jeu de rôle L5R² en fonction des points de compétence, de caractéristiques et de la difficulté. Chaque lancé de dé est répété un certain nombre de fois, par exemple 1000, afin de pouvoir faire un moyenne du taux de succès.

          Comme j'étais en train d'apprendre Haskell (je le suis toujours ;)) je me suis dit que c'était un bon entrainement que de l'écrire en Haskell.

          Voici mon code³, je ne pense pas qu'il soit illisible ou compliqué malgré le fait que ce soit de l'Haskell. Je vous laisse le comparer à la version Ruby¹.

          [1] http://www.gburri.org/bordel/l5r_roll.rb
          [2] http://l5r.alderac.com/
          [3] http://www.gburri.org/bordel/l5r_roll.hs
          • [^] # Re: La simplicité d'Haskell????

            Posté par . Évalué à 1.

            Interessant, moi aussi j'apprend toujours !

            J'ai joué aussi un peu avec les générateurs aléatoires, et au début ça m'a paru un peu déroutant de "trainer" la monade IO partout.

            Mais en fait c'était tout a fait logique, jusque que mon code se trouvait être entièrement dans la modade IO. Le programme en question est un embryon d'algorithme génétique, donc je pense que c'est la nature du problème elle même qui veut ça. Haskell à au moins le mérite de rendre tout cela explicite.


            Quelque remarques en vrac:

            J'aime bien definir une fonction (>>>) qui me permet d'enchainer les fonctions, un peu à la manière d'un pipe. Je trouve cela plus lisible que les composition à outrance

            x >>> f = f x

            a = [1..30] >>> map (+1) >>> filter odd >>> foldl (+) 0


            Par endroit dans ton code, j'aurai plutôt utilise la fonction liftM, que le raccourci x <- ...

            Sinon j'ai eu un petit problèmem avec la fontion "sequence", si tu peux m'eclairer un peu. J'ai l'impression que cette fonction n'est pas "lazy". Autrement dit si je l'applique sur une liste infinie, même pour n'en utiliser que les premiers elements, j'ai des petit problêmes de stack ;)
          • [^] # Re: La simplicité d'Haskell????

            Posté par . Évalué à 5.

            Il serait pas programmeur Java ton pote ?

            Je dis ça par-ce qu'il a rien compris a ruby. Exemple:

            Son code:

            def was_rolled ()
            if (@dices.length>0)
            return true;
            else
            return false;
            end
            end


            Celui d'un être normalement constitué:

            def was_rolled
            @dices.any?
            end


            On en viens même a ce demander si ça vaut le coup d'en faire une méthode, et ce n'est qu'un exemple parmi tant d'autres, et je n'ai pas envie d'essayer mais a mon avis on doit pouvoir rendre son code aussi concis que le tiens en Haskell.
            Donc plutôt que de mettre en cause Ruby je réfléchirai plutôt a la compétence de ton pote.
            • [^] # Re: La simplicité d'Haskell????

              Posté par . Évalué à 2.

              Oui, il est clair que le code Ruby pourrait être énormément amélioré et simplifié.

              Mais à mon avis il montre une chose intéressante avec l'approche orientée objet , c'est la manie de tout vouloir systématiquement modéliser, que ce soit en Ruby ou dans un autre langage OO.

              En exagérant beaucoup ça donne un peu ça :
              1. J'identifie mes objets
              2. Je définis des associations
              3. J'écris mes classes, leurs actions et leurs états
              4. Je réfléchis comment tout ce merdier va bien pouvoir coopérer pour résoudre mon problème initial...

              Bref, j'ai trouvé mon bonheur dans les langages fonctionnels, d'abord Erlang puis Haskell ;-)

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.