J'apprends la programmation fonctionnelle avec Haskell et j'essaye de saisir des concepts en comprenant d'abord pourquoi j'en ai besoin.
Je voudrais connaître l'objectif des flèches dans les langages de programmation fonctionnels. Quel problème résout-il? J'ai vérifié http://en.wikibooks.org/wiki/Haskell/Understanding_arrows et http://www.cse.chalmers.se/~rjmh/afp-arrows.pdf . Tout ce que je comprends, c'est qu'ils sont utilisés pour décrire des graphiques pour les calculs et qu'ils permettent un codage de style libre de points plus facile.
L'article suppose que le style sans point est généralement plus facile à comprendre et à écrire. Cela me semble assez subjectif. Dans un autre article ( http://en.wikibooks.org/wiki/Haskell/StephensArrowTutorial#Hangman:_Main_program ), un jeu du bourreau est implémenté, mais je ne vois pas comment les flèches rendent cette implémentation naturelle.
J'ai pu trouver beaucoup d'articles décrivant le concept, mais rien sur la motivation.
Qu'est-ce qui me manque?
Je me rends compte que je viens en retard à la fête, mais vous avez eu deux réponses théoriques ici, et je voulais fournir une alternative pratique à mâcher. J'y arrive en tant que parent Haskell qui a néanmoins récemment fait un pas de force dans le thème Arrows pour un projet sur lequel je travaille actuellement.
Tout d'abord, vous pouvez résoudre de manière productive la plupart des problèmes dans Haskell sans atteindre les flèches. Certains Haskellers notables ne les aiment vraiment pas et ne les utilisent pas (voir ici , ici , et ici pour plus d'informations)). Donc, si vous vous dites "Hé, je n'en ai pas besoin", comprenez que vous avez peut-être vraiment raison.
Ce que j'ai trouvé le plus frustrant à propos d'Arrows lorsque je les ai appris pour la première fois, c'est comment les didacticiels sur le sujet ont inévitablement atteint l'analogie des circuits. Si vous regardez le code Arrow - la variété sucrée, au moins - il ne ressemble en rien à un langage de définition de matériel. Vos entrées s'alignent à droite, vos sorties à gauche, et si vous ne les câblez pas toutes correctement, elles échouent tout simplement. Je me suis dit: Vraiment? Est-ce là que nous nous sommes retrouvés? Avons-nous créé un langage à un niveau si élevé qu'il se compose à nouveau de fils de cuivre et de soudure?
Pour autant que j'ai pu déterminer, la bonne réponse est la suivante: En fait, oui. Le cas d'utilisation du tueur en ce moment pour Arrows est FRP (pensez à Yampa, aux jeux, à la musique et aux systèmes réactifs en général). Le problème auquel FRP est confronté est en grande partie le même problème auquel sont confrontés tous les autres systèmes de messagerie synchrone: comment câbler un flux continu d'entrées dans un flux continu de sorties sans laisser tomber les informations pertinentes ou provoquer des fuites. Vous pouvez modéliser les flux sous forme de listes - plusieurs systèmes FRP récents utilisent cette approche - mais lorsque vous avez beaucoup d'entrées, les listes deviennent presque impossibles à gérer. Vous devez vous isoler du courant.
Ce que les flèches permettent dans les systèmes FRP, c'est la composition des fonctions dans un réseau tout en faisant abstraction entièrement de toute référence aux valeurs sous-jacentes transmises par ces fonctions. Si vous êtes nouveau dans la PF, cela peut être déroutant au début, puis époustouflant lorsque vous en avez absorbé les implications. Vous n'avez que récemment absorbé l'idée que les fonctions peuvent être abstraites et comment comprendre une liste comme [(*), (+), (-)]
comme étant de type [(a -> a -> a)]
. Avec les flèches, vous pouvez pousser l'abstraction d'une couche plus loin.
Cette capacité supplémentaire d'abstraction comporte ses propres dangers. D'une part, il peut pousser GHC dans des cas d'angle où il ne sait pas quoi faire de vos hypothèses de type. Vous devrez être prêt à penser au niveau du type - c'est une excellente occasion d'en apprendre davantage sur les types et les RankNTypes et d'autres sujets de ce type.
Il y a aussi un certain nombre d'exemples de ce que j'appellerais "Stupid Arrow Stunts" où le codeur recherche un combinateur Arrow juste parce qu'il ou elle veut montrer une astuce intéressante avec des tuples. (Voici le mien contribution insignifiante à la folie .) N'hésitez pas à ignorer un tel hot-dogging lorsque vous le rencontrez dans la nature.
REMARQUE: Comme je l'ai mentionné ci-dessus, je suis un noob relatif. Si j'ai promulgué des idées fausses ci-dessus, n'hésitez pas à me corriger.
C'est une sorte de réponse "douce", et je ne sais pas si une référence l'énonce réellement de cette manière, mais voici comment j'en viens à penser aux flèches:
Un type de flèche A b c
est essentiellement une fonction b -> c
mais avec plus de structure de la même manière qu'une valeur monadique M a
a plus de structure qu'un vieux a
.
Maintenant, ce que cette structure supplémentaire se trouve dépend de l'instance de flèche particulière dont vous parlez. Tout comme avec les monades IO a
et Maybe a
ont chacun une structure supplémentaire différente.
La chose que vous obtenez avec les monades est une incapacité à passer d'un M a
à un a
. Maintenant, cela peut sembler être une limitation, mais c'est en fait une fonctionnalité: le système de type vous protège de la transformation d'une valeur monadique en une ancienne valeur ordinaire. Vous ne pouvez utiliser la valeur qu'en participant à la monade via >>=
ou les opérations primitives de l'instance de monade particulière.
De même, la chose que vous obtenez de A b c
est une incapacité à construire une nouvelle "fonction" productrice de c consommatrice de b. La flèche vous protège contre la consommation de b
et la création d'un c
sauf en participant aux divers combinateurs de flèche ou en utilisant les opérations primitives de l'instance de flèche particulière.
Par exemple, les fonctions de signal dans Yampa sont à peu près (Time -> a) -> (Time -> b)
, mais en plus ils doivent obéir à une certaine restriction causalité: la sortie à l'instant t
est déterminée par les valeurs passées du signal d'entrée: vous ne pouvez pas regarder dans le futur . Donc ce qu'ils font c'est au lieu de programmer avec (Time -> a) -> (Time -> b)
, vous programmez avec SF a b
et vous construisez vos fonctions de signal à partir de primitives. Il se trouve que depuis SF a b
se comporte un peu comme une fonction, de sorte que la structure commune est ce qu'on appelle une "flèche".
J'aime à penser que les flèches, comme les monades et les foncteurs, permettent au programmeur de faire des compositions exotiques de fonctions.
Sans monades ou flèches (et foncteurs), la composition des fonctions dans un langage fonctionnel se limite à appliquer une fonction au résultat d'une autre fonction. Avec les monades et les foncteurs, vous pouvez définir deux fonctions, puis écrire du code réutilisable distinct qui spécifie comment ces fonctions, dans le contexte de la monade particulière, interagissent entre elles et avec les données qui leur sont transmises. Ce code est placé dans le code de liaison de la Monade. Ainsi, une monade est une vue unique, juste un conteneur pour le code de liaison réutilisable. Les fonctions se composent différemment dans le contexte d'une monade d'une autre monade.
Un exemple simple est la monade peut-être, où il y a du code dans la fonction de liaison de telle sorte que si une fonction A est composée avec une fonction B dans une monade peut-être, et B produit un rien, alors le code de liaison s'assurera que la composition de la deux fonctions produisent un Nothing, sans prendre la peine d'appliquer A à la valeur Nothing provenant de B. S'il n'y avait pas de monade, le programmeur devrait écrire du code dans A pour tester une entrée Nothing.
Les monades signifient également que le programmeur n'a pas besoin de taper explicitement les paramètres dont chaque fonction a besoin dans le code source - la fonction bind gère le passage des paramètres. Ainsi, en utilisant des monades, le code source peut commencer à ressembler davantage à une chaîne statique de noms de fonctions, plutôt qu'à ressembler à la fonction A qui "appelle" la fonction B avec les paramètres C et D - le code commence à ressembler plus à un circuit électronique qu'à un machine en mouvement - plus fonctionnelle qu'impérative.
Les flèches connectent également les fonctions avec une fonction de liaison, fournissant des fonctionnalités réutilisables et masquant les paramètres. Mais les flèches peuvent elles-mêmes être connectées ensemble et composées, et peuvent éventuellement router les données vers d'autres flèches au moment de l'exécution. Vous pouvez maintenant appliquer des données à deux chemins de flèches, qui "font des choses différentes" aux données, et remonter le résultat. Ou vous pouvez sélectionner la branche de flèches à laquelle transmettre les données, en fonction d'une certaine valeur dans les données. Le code résultant ressemble encore plus à un circuit électronique, avec des commutateurs, des retards, une intégration, etc. Le programme a l'air très statique et vous ne devriez pas voir beaucoup de manipulations de données en cours. Il y a de moins en moins de paramètres à penser et moins besoin de réfléchir aux valeurs que les paramètres peuvent ou non prendre.
L'écriture d'un programme Arrowized implique principalement de sélectionner des flèches standard telles que des séparateurs, des commutateurs, des retards et des intégrateurs, de lever des fonctions dans ces flèches et de connecter les flèches ensemble pour former des flèches plus grandes. Dans la programmation réactive fonctionnelle fléchie, les flèches forment une boucle, les entrées du monde étant combinées avec les sorties de la dernière itération du programme, de sorte que la sortie réagit aux entrées du monde réel.
L'une des valeurs du monde réel est le temps. Dans Yampa, la flèche de fonction de signal enfile de manière invisible le paramètre de temps dans le programme informatique - vous n'accédez jamais à la valeur de temps, mais si vous connectez une flèche d'intégrateur dans le programme, elle produira des valeurs intégrées au fil du temps que vous pouvez ensuite utiliser pour passer à d'autres flèches.
Juste un ajout aux autres réponses: Personnellement, cela m'aide beaucoup à comprendre ce qu'est un tel concept (mathématiquement) et comment il se rapporte à d'autres concepts que je connais.
Dans le cas des flèches, j'ai trouvé le document suivant utile - il compare les monades, les foncteurs applicatifs (idiomes) et les flèches: Les idiomes sont inconscients, les flèches sont méticuleuses, les monades sont promiscuous par Sam Lindley, Philip Wadler et Jeremy Yallop.
Je crois aussi que personne n'a mentionné ce lien qui peut vous fournir des idées et de la littérature sur le sujet.