Quelle est l'importance des concepts avancés de Haskell comme les monades et les foncteurs applicatifs pour la plupart des tâches de programmation de routine?


16

J'ai lu le livre Learn You a Haskell jusqu'au point où ils présentent des monades et des trucs comme Just a. Celles-ci sont tellement contre-intuitives pour moi que j'ai juste envie d'arrêter d'essayer de l'apprendre.

Mais je veux vraiment essayer.

Je me demande si je peux au moins éviter les concepts avancés pendant un certain temps et commencer à utiliser les autres parties du langage pour effectuer de nombreuses tâches pratiques avec les parties les plus élémentaires de Haskell, à savoir les fonctions, les E / S standard, la gestion des fichiers, interface de base de données et similaires.

Est-ce une approche raisonnable pour apprendre à utiliser Haskell?


2
Je ne sais pas jusqu'où tu iras. Sans monades, vous ne pouvez rien faire d'utile dans Haskell, car faire quoi que ce soit d'utile dans la programmation (comme dans l'écriture d'un programme sérieux que quelqu'un voudrait réellement utiliser) nécessite des E / S et un état, qui ne peuvent être gérés que dans Haskell grâce à l'utilisation de monades.
Mason Wheeler

1
@dan - J'ai finalement compris les monades Haskell en lisant deux documents - "Vous auriez pu inventer des monades" et "Tackling the Awkward Squad". Je ne peux pas dire s'ils sont meilleurs que "Learn You a Haskell", que je n'ai pas lu, mais ils ont fonctionné pour moi. Pour utiliser la notation do avec la monade IO, vous pouvez vous débrouiller avec très peu de compréhension de ce que sont les monades - vous ferez des choses qui feront rire ou pleurer les experts, mais superficiellement au moins, ce n'est pas très différent de l'utilisation un langage impératif conventionnel. Mais cela vaut la peine de mieux comprendre.
Steve314

2
@dan, il m'a fallu près d'une décennie pour m'ennuyer de la POO et commencer à apprécier / être curieux au sujet des langages fonctionnels. J'apprendrais encore F # et Clojure avant d'essayer sérieusement Haskell. Bien que les défis personnels soient agréables, il y a quelque chose à dire sur votre intuition et le chemin de moindre résistance. Cela dépend beaucoup de qui vous êtes. Si vous êtes un type fortement mathématique, vous aimeriez probablement Haskell / Schem dès le début. Si vous êtes plutôt du genre à faire les choses, c'est bien aussi. N'approchez pas Haskell avec une attitude «faire ou mourir». Continuez à apprendre d'autres choses, et quand vous vous ennuyez, revenez.
Job

Merci pour les conseils @Job. J'ai essayé Clojure un peu, mais la syntaxe Lisp ne fonctionne vraiment pas pour moi, à la fois pour la lecture et la frappe. Je trouve la syntaxe de Haskell plus naturelle. Je suis heureux d'utiliser Ruby pour des projets en attendant, mais j'espère commencer à faire des projets pratiques à Haskell.
dan

@dan, drôle, je trouve la syntaxe de Clojure plutôt sympathique, mais c'est probablement parce que j'ai été exposé à Scheme avant. Peut-être qu'après F # je m'habituerai à la façon dont Haskell fait les choses.
Job

Réponses:


16

Distinguons d'abord entre l'apprentissage des concepts abstraits et l'apprentissage d'exemples spécifiques d'entre eux.

Vous n'irez pas très loin en ignorant tous les exemples spécifiques, pour la simple raison qu'ils sont tout à fait omniprésents. En fait, les abstractions existent en grande partie parce qu'elles unifient les choses que vous feriez de toute façon avec les exemples spécifiques.

Les abstractions elles-mêmes, en revanche, sont certainement utiles , mais elles ne sont pas immédiatement nécessaires. Vous pouvez obtenir assez loin en ignorant complètement les abstractions et en utilisant directement les différents types. Vous voudrez éventuellement les comprendre, mais vous pourrez toujours y revenir plus tard. En fait, je peux presque garantir que si vous faites cela, lorsque vous y reviendrez, vous vous claquerez le front et vous vous demanderez pourquoi vous avez passé tout ce temps à faire les choses à la dure au lieu d'utiliser les outils polyvalents pratiques.

Prenons Maybe al'exemple. C'est juste un type de données:

data Maybe a = Just a | Nothing

C'est tout sauf auto-documenté; c'est une valeur facultative. Soit vous avez "juste" quelque chose de type a, soit vous n'avez rien. Supposons que vous ayez une fonction de recherche quelconque, qui revient Maybe Stringà représenter la recherche d'une Stringvaleur qui peut ne pas être présente. Donc, votre motif correspond à la valeur pour voir de quelle valeur il s'agit:

case lookupFunc key of
    Just val -> ...
    Nothing  -> ...

C'est tout!

Vraiment, vous n'avez besoin de rien d'autre. Pas de Functors ou Monads ou autre chose. Ceux-ci expriment des façons courantes d'utiliser des Maybe avaleurs ... mais ce ne sont que des idiomes, des "modèles de conception", peu importe comment vous pouvez l'appeler.

Le seul endroit où vous ne pouvez vraiment pas l'éviter complètement est IO, mais c'est une mystérieuse boîte noire de toute façon, donc cela ne vaut pas la peine d'essayer de comprendre ce que cela signifie en tant que Monadou quoi que ce soit.

En fait, voici une feuille de triche pour tout ce que vous devez vraiment savoir IOpour l'instant:

  • Si quelque chose a un type IO a, cela signifie que c'est une procédure qui fait quelque chose et crache une avaleur.

  • Lorsque vous avez un bloc de code utilisant la donotation, écrivez quelque chose comme ceci:

    do -- ...
       inp <- getLine
       -- etc...
    

    ... signifie exécuter la procédure à droite de <-et affecter le résultat au nom à gauche.

  • Alors que si vous avez quelque chose comme ça:

    do -- ...
       let x = [foo, bar]
       -- etc...
    

    ... cela signifie affecter la valeur de l'expression simple (pas une procédure) à droite de la =au nom à gauche.

  • Si vous y mettez quelque chose sans assigner de valeur, comme ceci:

    do putStrLn "blah blah, fishcakes"
    

    ... cela signifie exécuter une procédure et ignorer tout ce qu'elle retourne. Certaines procédures ont le type IO ()- le ()type est une sorte d'espace réservé qui ne dit rien, ce qui signifie simplement que la procédure fait quelque chose et ne renvoie pas de valeur. Un peu comme une voidfonction dans d'autres langues.

  • L'exécution de la même procédure plusieurs fois peut donner des résultats différents; c'est un peu l'idée. C'est pourquoi il n'y a aucun moyen de "supprimer" IOune valeur, car quelque chose IOn'est pas une valeur, c'est une procédure pour obtenir une valeur.

  • La dernière ligne d'un dobloc doit être une procédure simple sans affectation, où la valeur de retour de cette procédure devient la valeur de retour pour le bloc entier. Si vous souhaitez que la valeur de retour utilise une valeur déjà affectée, la returnfonction prend une valeur simple et vous donne une procédure sans opération qui renvoie cette valeur.

  • À part cela, il n'y a rien de spécial IO; ces procédures sont en fait des valeurs simples et vous pouvez les transmettre et les combiner de différentes manières. Ce n'est que lorsqu'ils sont exécutés dans un dobloc appelé quelque part mainqu'ils font n'importe quoi.

Donc, dans quelque chose comme ce programme d'exemple stéréotypé totalement ennuyeux:

hello = do putStrLn "What's your name?"
           name <- getLine
           let msg = "Hi, " ++ name ++ "!"
           putStrLn msg
           return name

... vous pouvez le lire comme un programme impératif. Nous définissons une procédure nommée hello. Une fois exécuté, il exécute d'abord une procédure pour imprimer un message vous demandant votre nom; ensuite, il exécute une procédure qui lit une ligne d'entrée et affecte le résultat à name; puis il attribue une expression au nom msg; puis il imprime le message; puis il renvoie le nom de l'utilisateur comme résultat de l'ensemble du bloc. Puisque nameest un String, cela signifie que helloc'est une procédure qui renvoie un String, donc il a du type IO String. Et maintenant, vous pouvez exécuter cette procédure ailleurs, tout comme elle s'exécute getLine.

Pfff, monades. Qui en a besoin?


2
@dan: Vous êtes les bienvenus! Si vous avez des questions spécifiques en cours de route, n'hésitez pas à demander sur Stack Overflow. La balise [haskell] y est généralement assez active, et si vous expliquez d'où vous venez, les gens sont plutôt bons pour vous aider à comprendre plutôt que de simplement lancer des réponses cryptiques.
CA McCann

9

Quelle est l'importance des concepts avancés de Haskell comme les monades et les foncteurs applicatifs pour la plupart des tâches de programmation de routine?

Ils sont essentiels. Sans eux, il est trop facile d'utiliser Haskell comme un autre langage impératif, et bien que Simon Peyton Jones ait appelé Haskell, "le meilleur langage de programmation impératif du monde", vous manquez toujours la meilleure partie.

Les monades, les transformées de monade, les monoïdes, les foncteurs, les foncteurs applicatifs, les foncteurs contraires, les flèches, les catégories, etc., sont des modèles de conception. Une fois que vous avez intégré la chose spécifique que vous créez dans l'une d'elles, vous pouvez utiliser une énorme richesse de fonctions écrites de manière abstraite. Ces classes de types ne sont pas simplement là pour vous dérouter, elles sont là pour vous faciliter la vie et vous rendre plus productif. Ils sont, à mon avis, la principale raison de la concision du bon code Haskell.


1
+1: Merci d'avoir souligné que "les monades, les transformées de monade, les monoïdes, les foncteurs, les foncteurs applicatifs, les foncteurs contraires, les flèches, les catégories, etc., sont des modèles de conception." !
Giorgio

7

Est-ce strictement nécessaire si vous voulez simplement faire fonctionner quelque chose? Non.

Est-ce nécessaire si vous voulez écrire de beaux programmes Haskell idiomatiques? Absolument.

Lors de la programmation dans Haskell, il est utile de garder à l'esprit deux choses:

  1. Le système de typage est là pour vous aider. Si vous composez votre programme à partir de code très polymorphe et lourd de classes, vous pouvez très souvent vous retrouver dans une situation merveilleuse où le type de fonction que vous souhaitez vous guidera vers la (une seule!) Implémentation correcte .

  2. Les différentes bibliothèques disponibles ont été développées en utilisant le principe # 1.

Donc, lorsque j'écris un programme en Haskell, je commence par écrire les types. Ensuite, je recommence et j'écris quelques types plus généraux (et s'ils peuvent être des instances de classes de caractères existantes, tant mieux). Ensuite, j'écris quelques fonctions qui m'obtiennent des valeurs des types que je veux. (Ensuite, je joue avec eux ghciet j'écris peut-être des tests QuickCheck.) Et enfin, je colle tout cela ensemble main.

Il est possible d'écrire Haskell laid qui fait juste fonctionner quelque chose. Mais il y a tellement plus à apprendre une fois que vous avez atteint ce stade.

Bonne chance!


1
Je vous remercie! J'ai hésité entre Clojure et Haskell, mais maintenant je penche vers Haskell à cause de la façon dont les gens comme vous ont été utiles.
dan

1
La communauté Haskell semble être très gentille et serviable. Et il semble que c'est de là que viennent beaucoup d'idées intéressantes
Zachary K
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.