web-dev-qa-db-fra.com

Explication des combinateurs pour l'homme au travail

Qu'est-ce qu'un combinateur ??

Est-ce "une fonction ou une définition sans variable libre" (tel que défini sur SO)?

Ou que diriez-vous de cela: selon John Hughes dans son article bien connu sur les flèches, "un combinateur est une fonction qui construit des fragments de programme à partir de fragments de programme" , ce qui est avantageux car "... le programmeur utilisant des combinateurs construit une grande partie du programme souhaité automatiquement, plutôt que d'écrire chaque détail à la main". Il ajoute que map et filter sont deux exemples courants de tels combinateurs.

Quelques combinateurs qui correspondent à la première définition:

Quelques combinateurs qui correspondent à la deuxième définition:

  • carte
  • filtre
  • plier/réduire (vraisemblablement)
  • l'un des >> =, composer, fmap ?????

Je ne suis pas intéressé par la première définition - cela ne m'aiderait pas à écrire un vrai programme (+1 si vous me convaincez que je me trompe). Veuillez m'aider à comprendre la deuxième définition . Je pense que mapper, filtrer et réduire sont utiles: ils me permettent de programmer à un niveau supérieur - moins d'erreurs, un code plus court et plus clair. Voici certaines de mes questions spécifiques sur les combinateurs:

  1. Quels sont les autres exemples de combinateurs tels que carte, filtre?
  2. Quels combinateurs les langages de programmation implémentent-ils souvent?
  3. Comment les combinateurs peuvent-ils m'aider à concevoir une meilleure API?
  4. Comment concevoir des combinateurs efficaces?
  5. À quoi ressemblent les combinateurs dans un langage non fonctionnel (par exemple, Java), ou à quoi ces langages utilisent-ils à la place des combinateurs?

Mise à jour

Merci à @C. A. McCann, j'ai maintenant une meilleure compréhension des combinateurs. Mais une question est encore un point critique pour moi:

Quelle est la différence entre un programme fonctionnel écrit avec et un programme écrit sans utilisation intensive de combinateurs?

Je soupçonne que la réponse est que la version lourde de combinateur est plus courte, plus claire, plus générale, mais j'apprécierais une discussion plus approfondie, si possible.

Je recherche également plus d'exemples et d'explications sur les combinateurs complexes (c'est-à-dire plus complexes que fold) dans les langages de programmation courants.

90
Matt Fenwick

Je ne suis pas intéressé par la première définition - cela ne m'aiderait pas à écrire un vrai programme (+1 si vous me convaincez que je me trompe). Veuillez m'aider à comprendre la deuxième définition. Je pense que mapper, filtrer et réduire sont utiles: ils me permettent de programmer à un niveau supérieur - moins d'erreurs, un code plus court et plus clair.

Les deux définitions sont fondamentalement la même chose. Le premier est basé sur la définition formelle et les exemples que vous donnez sont combinateurs primitifs - les plus petits blocs de construction possibles. Ils peuvent vous aider à écrire un vrai programme dans la mesure où, avec eux, vous pouvez construire des combinateurs plus sophistiqués. Considérez les combinateurs comme S et K comme le langage machine d'un "ordinateur combinatoire" hypothétique. Les ordinateurs réels ne fonctionnent pas de cette façon, bien sûr, donc dans la pratique, vous aurez généralement des opérations de niveau supérieur implémentées dans les coulisses d'autres manières, mais la base conceptuelle est toujours un outil utile pour comprendre le sens de ces opérations de niveau supérieur.

La deuxième définition que vous donnez est plus informelle et concerne l'utilisation de combinateurs plus sophistiqués, sous la forme de fonctions d'ordre supérieur qui combinent d'autres fonctions de diverses manières. Notez que si les blocs de construction de base sont les combinateurs primitifs ci-dessus, tout construit à partir d'eux est une fonction d'ordre supérieur et un combinateur également. Dans un langage où d'autres primitives existent, cependant, vous faites une distinction entre les choses qui sont ou ne sont pas des fonctions, auquel cas un combinateur est généralement défini comme une fonction qui manipule d'autres fonctions de manière générale, plutôt que d'opérer sur des non faire fonctionner les choses directement.

Quels sont les autres exemples de combinateurs tels que carte, filtre?

Beaucoup trop pour les énumérer! Ces deux transforment une fonction qui décrit le comportement d'une seule valeur en une fonction qui décrit le comportement d'une collection entière. Vous pouvez également avoir des fonctions qui transforment niquement d'autres fonctions, telles que leur composition de bout en bout, ou le fractionnement et la recombinaison d'arguments. Vous pouvez avoir des combinateurs qui transforment les opérations en une seule étape en opérations récursives qui produisent ou consomment des collections. Ou toutes sortes d'autres choses, vraiment.

Quels combinateurs les langages de programmation implémentent-ils souvent?

Cela va varier un peu. Il y a relativement peu de combinateurs complètement génériques - principalement les primitifs mentionnés ci-dessus - donc dans la plupart des cas, les combinateurs auront une certaine connaissance des structures de données utilisées (même si ces structures de données sont construites à partir d'autres combinateurs de toute façon), dans lesquelles Dans ce cas, il existe généralement une poignée de combinateurs "entièrement génériques", puis toutes les formes spécialisées que quelqu'un a décidé de fournir. Il existe un nombre ridicule de cas où (des versions convenablement généralisées de) mapper, plier et déplier suffisent pour faire presque tout ce que vous pourriez souhaiter.

Comment les combinateurs peuvent-ils m'aider à concevoir une meilleure API?

Exactement comme vous l'avez dit, en pensant en termes d'opérations de haut niveau et de la façon dont celles-ci interagissent, au lieu de détails de bas niveau.

Pensez à la popularité des boucles de style "pour chaque" sur les collections, qui vous permettent d'abstraire les détails de l'énumération d'une collection. Ce ne sont que des opérations de mappage/pliage dans la plupart des cas, et en faisant un combinateur (plutôt que la syntaxe intégrée), vous pouvez faire des choses telles que prendre deux boucles existantes et les combiner directement de plusieurs manières - imbriquer l'une dans l'autre, faire les uns après les autres, et ainsi de suite - en appliquant simplement un combinateur, plutôt qu'en jonglant avec tout un tas de code.

Comment concevoir des combinateurs efficaces?

Tout d'abord, réfléchissez aux opérations qui ont du sens sur les données utilisées par votre programme. Réfléchissez ensuite à la façon dont ces opérations peuvent être combinées de manière significative de manière générique, ainsi qu'à la façon dont les opérations peuvent être décomposées en morceaux plus petits qui sont connectés les uns aux autres. L'essentiel est de travailler avec transformations et opérations, pas directement actions. Lorsque vous avez une fonction qui fait juste un peu de fonctionnalité compliquée de manière opaque et ne crache qu'une sorte de résultat pré-digéré, vous ne pouvez pas faire grand-chose avec cela. Laissez les résultats finaux au code qui tilise les combinateurs - vous voulez des choses qui vous mènent du point A au point B, pas des choses qui devraient être le début ou la fin d'un processus.

À quoi ressemblent les combinateurs dans un langage non fonctionnel (par exemple, Java), ou à quoi ces langages utilisent-ils à la place des combinateurs?

Ahahahaha. C'est drôle, vous devriez demander, parce que les objets sont vraiment des objets d'ordre supérieur en premier lieu - ils ont des données, mais ils portent également un tas d'opérations, et pas mal de ce qui constitue une bonne conception OOP se résume à "les objets devraient généralement agir comme des combinateurs, pas des structures de données".

Donc, probablement la meilleure réponse ici est qu'au lieu de choses de type combinateur, ils utilisent des classes avec beaucoup de méthodes getter et setter ou des champs publics, et une logique qui consiste principalement à faire une action opaque et prédéfinie.

92
C. A. McCann