Le rêve d'une programmation déclarative [clôturé]

26

Pourquoi le rêve d'une programmation déclarative ne s'est-il pas réalisé? Quels obstacles concrets entravent-ils? Pour un exemple simple, pourquoi ne puis-je pas dire

sort(A) is defined by sort(A) in perm(A) && asc(sort(A))

et en extraire automatiquement un algorithme de tri. permsignifie permutations et ascsignifie ascendant.

davidk01
la source
4
Au fait, votre exemple spécifique est déjà disponible: gkoberger.github.io/stacksort
Den
3
Avez-vous entendu parler de Prolog? Recherchez simplement "Answer Set Programming". Il existe de nombreux systèmes basés sur la logique par défaut.
schlingel
16
Eh bien, cette question est facile à répondre. Essayez d'implémenter un tel système . Qu'est-ce qui vous a empêché de le faire avec succès? Les chances sont bonnes que tout ce qui vous a arrêté a arrêté tout le monde.
Eric Lippert
4
Je suis tenté de croire que cette question mérite plus de crédit qu'elle n'en reçoit. À première vue, vous pourriez penser: « Eh bien, c'est simple! Vous devez programmer toute cette logique derrière cela, et les ordinateurs ne sont tout simplement pas si intelligents. ... Mais ensuite vous revenez et jetez un deuxième coup d'œil à cette question, et vous pensez encore une fois, Eh bien, oui, c'est simple, et vous devez programmer toute cette logique - et les ordinateurs ne sont pas nécessairement les outils les plus pointus dans l'abri, c'est vrai - mais il y a beaucoup plus de profondeur dans cette explication que ce qui se trouve simplement à la surface.
Panzercrisis
3
Votre description d'un algorithme de tri est déclarative, oui, mais elle n'est certainement pas efficace. Il existe des n!permutations d'une séquence et dans le pire des cas, votre algorithme devra les essayer toutes pour en trouver une triée. Le temps factoriel est à peu près aussi mauvais qu'un algorithme pour traiter une séquence peut le faire.
Benjamin Hodgson

Réponses:

8

Il y a de très bonnes réponses. Je vais essayer de contribuer à la discussion.

Au sujet de la programmation déclarative et logique dans Prolog, il y a le grand livre "The Craft of Prolog" de Richard O'Keefe . Il s'agit d'écrire des programmes efficaces en utilisant un langage de programmation qui vous permet d'écrire des programmes très inefficaces. Dans cet ouvrage, tout en discutant des implémentations efficaces de plusieurs algorithmes (dans le chapitre "Méthodes de programmation"), l'auteur adopte l'approche suivante:

  • définir le problème en anglais
  • écrire une solution de travail aussi déclarative que possible; généralement, cela signifie à peu près exactement ce que vous avez dans votre question, juste corriger Prolog
  • à partir de là, prenez des mesures pour affiner la mise en œuvre pour la rendre plus rapide

L'observation la plus éclairante (pour moi) que j'ai pu faire en travaillant à travers ceux-ci:

Oui, la version finale de l'implémentation est beaucoup plus efficace que la spécification "déclarative" avec laquelle l'auteur a commencé. Il est toujours très déclaratif, succinct et facile à comprendre. Ce qui s'est passé entre les deux, c'est que la solution finale capture les propriétés du problème auquel la solution initiale était inconsciente.

En d'autres termes, lors de la mise en œuvre d'une solution, nous avons utilisé autant que possible nos connaissances sur le problème. Comparer:

Trouver une permutation d'une liste telle que tous les éléments sont dans l'ordre croissant

à:

La fusion de deux listes triées entraînera une liste triée. Puisqu'il peut y avoir des sous-listes qui sont déjà triées, utilisez-les comme point de départ, au lieu de sous-listes de longueur 1.

Petit bémol: une définition comme celle que vous avez donnée est séduisante car elle est très générale. Cependant, je ne peux pas échapper au sentiment qu'il ignore délibérément le fait que les permutations sont, bien, un problème combinatoire. C'est quelque chose que nous savons déjà ! Ce n'est pas une critique, juste une observation.

Quant à la vraie question: comment avancer? Eh bien, une façon consiste à fournir autant de connaissances sur le problème que nous déclarons à l'ordinateur.

La meilleure tentative que je connaisse pour vraiment résoudre le problème est présentée dans les livres co-écrits par Alexander Stepanov, "Elements of Programming" et "From Mathematics to Generic Programming" . Je ne suis malheureusement pas à la hauteur de tout résumer (ou même de bien comprendre) tout dans ces livres. Cependant, l'approche consiste à définir des algorithmes de bibliothèque et des structures de données efficaces (voire optimaux), sous réserve que toutes les propriétés pertinentes de l'entrée soient connues à l'avance. Le résultat final est:

  • Chaque transformation bien définie est un raffinement des contraintes déjà en place (les propriétés connues);
  • Nous laissons l'ordinateur décider de la transformation optimale en fonction des contraintes existantes.

Quant à savoir pourquoi cela ne s'est pas encore produit, eh bien, l'informatique est un domaine très jeune, et nous devons encore vraiment apprécier la nouveauté de la plupart d'entre eux.

PS

Pour vous donner un avant-goût de ce que je veux dire par "affiner l'implémentation": prenez par exemple le problème facile d'obtenir le dernier élément d'une liste, dans Prolog. La solution déclarative canonique est de dire:

last(List, Last) :-
    append(_, [Last], List).

Ici, la signification déclarative de append/3est:

List1AndList2est la concaténation List1etList2

Étant donné que dans le deuxième argument, append/3nous avons une liste avec un seul élément et que le premier argument est ignoré (le trait de soulignement), nous obtenons un fractionnement de la liste d'origine qui supprime le début de la liste ( List1dans le contexte de append/3) et exige que le dos ( List2dans le contexte de append/3) est en effet une liste avec un seul élément: c'est donc le dernier élément.

La mise en œuvre réelle fournie par SWI-Prolog , cependant, dit:

last([X|Xs], Last) :-
    last_(Xs, X, Last).

last_([], Last, Last).
last_([X|Xs], _, Last) :-
    last_(Xs, X, Last).

C'est encore bien déclaratif. Lisez de haut en bas:

Le dernier élément d'une liste n'a de sens que pour une liste d'au moins un élément. Le dernier élément pour une paire de queue et la tête d'une liste est donc: la tête, quand la queue est vide, ou la dernière de la queue non vide.

La raison pour laquelle cette implémentation est fournie est de contourner les problèmes pratiques entourant le modèle d'exécution de Prolog. Idéalement, cela ne devrait pas faire de différence quant à l'implémentation utilisée. De même, nous aurions pu dire:

last(List, Last) :-
    reverse(List, [Last|_]).

Le dernier élément d'une liste est le premier élément de la liste inversée.

Si vous souhaitez obtenir un remplissage de discussions non concluantes sur ce qui est bon, déclaratif Prolog, passez simplement par certaines des questions et réponses dans la balise Prolog sur Stack Overflow .

XXX
la source
2
+1 pour montrer comment une conception déclarative peut passer d'une simple abstraction à une implémentation plus concrète via un processus de conception itérative.
itsbruce
1
@Boris C'est une bonne réponse. Ce livre est posé sur ma bibliothèque. Il est probablement temps que je l'ouvre.
davidk01
1
@ davidk01 L'un des meilleurs livres là-bas. Cela suppose que vous êtes assez à l'aise avec Prolog et la programmation en général, mais l'approche qu'elle adopte en matière de programmation est à la fois pragmatique et très approfondie.
XXX
2
@Boris Je sais que l'exemple n'est pas complexe mais la productivité du processus de conception itérative - une vraie force des langages déclaratifs - et sa valeur très pratique, est cruciale. Les langages déclaratifs offrent une approche claire, cohérente et récursive de l'amélioration itérative. Les langues impératives ne le font pas.
itsbruce
1
+1 pour "faites le plein de discussions peu concluantes sur ce qui est bon, Prolog déclaratif"… très vrai que nous avons tendance à être en désaccord!
Daniel Lyons
50

Les langues logiques le font déjà. Vous pouvez définir le tri de la même manière que vous le faites.

Le principal problème est la performance. Les ordinateurs peuvent être excellents pour calculer beaucoup de choses, mais ils sont intrinsèquement stupides. Chaque décision «intelligente» qu'un ordinateur pouvait prendre était programmée par un programmeur. Et cette décision est généralement décrite non pas par l'apparence du résultat final, mais par la façon d'atteindre, étape par étape, ce résultat final.

Imaginez l'histoire d'un Golem . Si vous essayez de lui donner un ordre abstrait, alors au mieux, il le fera de manière inefficace et au pire, se blessera, vous ou quelqu'un d'autre. Mais si vous décrivez ce que vous voulez dans les moindres détails, vous êtes assuré que la tâche sera accomplie de manière efficace et efficiente.

C'est le travail du programmeur de décider du niveau d'abstraction à utiliser. Pour l'application que vous créez, allez-vous aller de haut niveau et la décrire de manière abstraite et prendre les performances ou aller bas et sale, y passer 10 fois plus de temps, mais obtenir un algorithme 1000 fois plus performant?

Euphorique
la source
6
Il pourrait être utile de savoir que le mot Golem גולם signifie en fait "matière première", c'est-à-dire l'état le plus élémentaire auquel la machine / entité peut se trouver.
dotancohen
2
Les langages déclaratifs ne sont pas intrinsèquement un obstacle à des niveaux d'abstraction inférieurs. Haskell et Standard ML vous permettent à la fois de faire des déclarations déclaratives simples sur les types / fonctions en un seul endroit, de fournir une gamme d'implémentations de fonctions concrètes et spécifiques dans un endroit séparé et des façons de faire correspondre les types aux implémentations dans un autre encore. Pendant ce temps, les meilleures pratiques dans les langages OO / impératifs consistent désormais à démarrer haut / simple, puis à ajouter des détails d'implémentation. La différence est que l'abstraction élevée est plus facile en PF, le niveau bas est plus facile impérativement.
itsbruce
2
Devrait dire qu'il est également possible, dans les deux langues mentionnées, de résoudre automatiquement le choix de l'implémentation spécifique en fonction des propriétés du type plutôt que des correspondances spécifiques au codage en dur, ce qui fournit à peu près ce que l'OP veut. Dans Haskell, les classes de type seraient un outil clé pour cela. En Standard ML, foncteurs.
itsbruce
22
@BAR Golem! = Golum Golem est du folklore juif
Euphoric
10
Ma leçon à retenir de cette réponse est d'écrire אמת sur mon ordinateur portable.
Dan J
45

En plus de l'excellent point d'Euphoric , je voudrais ajouter que nous utilisons déjà des langages déclaratifs dans de nombreux endroits où ils fonctionnent bien, c'est-à-dire décrire un état qui ne changera probablement pas ou demander quelque chose pour lequel l'ordinateur peut réellement générer du code efficace seul:

  • HTML déclare quel est le contenu d'une page Web.

  • CSS déclare à quoi devraient ressembler les différents types d'éléments d'une page Web.

  • Chaque base de données relationnelle possède un langage de définition de données qui déclare quelle est la structure de la base de données.

  • SQL est beaucoup plus proche du déclaratif que de l'impératif, car vous lui dites ce que vous voulez voir et le planificateur de requêtes de la base de données détermine comment y arriver.

  • On pourrait soutenir que la plupart des fichiers de configuration (.vimrc, .profile, .bashrc, .gitconfig) utilisent un langage spécifique au domaine qui est largement déclaratif

Ixrec
la source
3
Je mentionnerai GNU Make, XSLT, Angular.js comme des choses largement utilisées qui sont également déclaratives (bien que angulaire pousse peut-être un peu la définition).
Mark K Cowan
Permettez-moi d'ajouter des expressions régulières à cette liste.
Schwern
7
Les gens ont tendance à oublier que les langages déclaratifs sont courants . Ce ne sont généralement pas des langues complètes. Ajoutez l'expression régulière à cette liste.
slebetman
Un peu pédant, mais quand même: toutes les bases de données n'ont pas de DDL, pensez au grand nombre de bases de données NoSQL sans schémas. Chaque base de données relationnelle peut avoir, mais pas toutes les bases de données.
Rétablir Monica - dirkk
1
@dirkk n'y avait pas pensé. Correction de ma réponse.
Ixrec
17

Les abstractions fuient

Vous pouvez implémenter un système déclaratif où vous déclarez ce que vous voulez, et le compilateur ou l'interpréteur détermine un ordre d'exécution. L'avantage théorique est qu'il vous libère de la nécessité de réfléchir au «comment», et vous n'avez pas à détailler cette implémentation. Cependant, dans la pratique pour l'informatique à usage général, vous devez toujours penser au `` comment '' et écrire toutes sortes d'astuces tout en gardant à l'esprit comment cela sera implémenté, car sinon le compilateur peut (et souvent choisit) choisir une implémentation qui sera très, très, très lent (par exemple n! opérations où n suffirait).

Dans votre exemple particulier, vous obtiendrez un algorithme de tri - cela ne signifie pas que vous en obtiendrez un bon ou même un peu utilisable. Votre définition donnée, si elle est implémentée littéralement (comme le ferait probablement un compilateur), entraîne http://en.wikipedia.org/wiki/Bogosort qui est inutilisable pour les jeux de données plus volumineux - elle est techniquement correcte, mais a besoin d'une éternité pour trier un millier de nombres .

Pour certains domaines limités, vous pouvez écrire des systèmes qui réussissent presque toujours bien à trouver une bonne implémentation, par exemple SQL. Pour l'informatique à usage général qui ne fonctionne pas particulièrement bien - vous pouvez écrire des systèmes dans, disons, Prolog mais vous devez visualiser comment exactement vos déclarations seront converties en un ordre d'exécution impératif à la fin, et cela perd une grande partie du déclaratif attendu avantages de la programmation.

Peter est
la source
Alors que ce que vous dites est essentiellement vrai, de mauvaises performances ne sont pas le signe d'une abstraction qui fuit à moins que l'interface / le contrat ne vous fournisse des garanties, par exemple sur le temps d'exécution.
valenterry
3
Peters ne dit pas qu'une mauvaise performance est un signe d'abstraction qui fuit, @valenterry. Au contraire, il dit le contraire: que pour obtenir de bonnes performances, les détails de mise en œuvre sont forcés de fuir.
itsbruce
2
Je pense qu'il est trompeur de dire que les abstractions fuient simplement parce que vous devez comprendre l'implémentation pour comprendre comment elle affecte les performances. Le but d'une abstraction n'est pas de vous empêcher d'avoir à penser aux performances.
Doval
1
@jamesqf Dans la programmation déclarative, vous déclarez simplement que quelque chose est trié. Vous pouvez déclarer que l'ordre de tri est lié à une variable / propriété. Et il en serait ainsi. Pas besoin d'appeler explicitement le tri chaque fois que de nouvelles données sont ajoutées ou que l'ordre de tri change.
hyde
1
@jamesqf Vous ne pouvez pas vraiment voir le point sans vraiment vous essayer (je recommanderais QML de Qt pour jouer avec des idées déclaratives). Imaginez quelqu'un qui ne connaît que la programmation impérative et qui essaie de comprendre l'intérêt de la programmation orientée objet ou de la programmation fonctionnelle sans vraiment l'essayer pour de vrai.
hyde
11

La décidabilité informatique est la raison la plus importante pour laquelle la programmation déclarative ne s'est pas avérée aussi simple qu'elle semble l'être.

De nombreux problèmes relativement faciles à énoncer se sont révélés indécidables ou ont une complexité NP-complète à résoudre. Cela se produit souvent lorsque nous prenons en compte les classes négatives et la classification, la comptabilité et la récursivité.

Je voudrais expliquer cela avec certains domaines bien connus.

La décision sur la classe CSS à utiliser nécessite une connaissance et une prise en compte de toutes les règles CSS. L'ajout de nouvelles règles peut invalider toutes les autres décisions. Les classes CSS négatives ne sont pas intentionnellement ajoutées au langage, en raison de problèmes NP-complets, mais le manque de classes négatives complique les décisions de conception CSS.

Dans un optimiseur de requête (SQL), il y a le problème délicat de décider dans quel ordre se joindre, quels indices utiliser et quelle mémoire allouer à quels résultats temporaires. Il s'agit d'un problème NP-complete connu qui complique la conception de la base de données et la formulation des requêtes. Pour le formuler différemment: lors de la conception d'une base de données ou d'une requête, le concepteur doit connaître les actions et l'ordre des actions que l'optimiseur de requêtes est susceptible de prendre. Un ingénieur expérimenté a besoin de connaître l'heuristique utilisée par les principaux fournisseurs de bases de données.

Les fichiers de configuration sont déclaratifs, mais certaines configurations sont difficiles à déclarer. Par exemple, pour configurer correctement les fonctionnalités, il faut prendre en compte la gestion des versions, le déploiement (et l'historique du déploiement), les remplacements manuels possibles et les conflits possibles avec d'autres paramètres. Valider correctement une configuration peut devenir un problème NP-complet.

Le résultat est que ces complications prennent les débutants par surprise, elles brisent la «beauté» de la programmation déclarative et obligent certains ingénieurs à rechercher d'autres solutions. La migration d'ingénieurs inexpérimentés de SQL vers NoSQL pourrait avoir été déclenchée par la complexité sous-jacente des bases de données relationnelles.

Dibbeke
la source
2
"Les classes CSS négatives ne sont pas intentionnellement ajoutées au langage, à cause de problèmes NP-complets" - pouvez-vous élaborer?
John Dvorak
C'est un peu un exercice, mais avec des sélecteurs CSS négatifs, il est possible de les réécrire en un problème 3SAT (avec la dernière clause étant le DOM), ce qui nécessiterait d'essayer toutes les combinaisons possibles, pour voir s'il correspond.
Dibbeke
1
Petit ajout. En CSS 3 et 4, les sélecteurs négatifs sont autorisés, mais: les pseudo-classes ne peuvent pas être imbriquées.
Dibbeke
2

Nous avons une différence de déclarativité des langages de programmation qui est mise à profit dans la vérification de la logique numérique.

Normalement, la logique numérique est décrite au niveau de transfert de registre (RTL) où le niveau logique des signaux entre les registres est défini. Pour vérifier que nous ajoutons de plus en plus de propriétés définies de manière plus abstraite et déclarative.

L'un des langages / sous-ensembles de langues les plus déclaratifs est appelé PSL pour Property Specification Language. Lors du test d'un modèle RTL d'un multiplicateur dans lequel, par exemple, toutes les opérations logiques de décalage et d'addition sur plusieurs cycles d'horloge doivent être spécifiées; vous pouvez écrire une propriété à la manière de assert that when enable is high, this output will equal the multiplication of these two inputs after no more than 8 clock cycles. La description PSL peut ensuite être vérifiée avec le RTL dans une simulation, ou le PSL peut être formellement prouvé pour la description RTL.

Le modèle PSL plus déclaratif oblige à décrire le même comportement que la description RTL mais d'une manière suffisamment différente qui peut être vérifiée automatiquement par rapport au RTL pour voir si elles sont d'accord.

Paddy3118
la source
1

Le problème est surtout de savoir comment vous modélisez les données; et la programmation déclarative n'aide pas ici. Dans les langues impératives, vous avez déjà des tonnes de bibliothèques qui font beaucoup de choses pour vous, vous n'avez donc qu'à savoir quoi appeler. D'une manière particulière, on pourrait considérer cette programmation déclarative (probablement le meilleur exemple pour cela est Stream API en Java 8 ). Ayant cela, l'abstraction est déjà résolue et la programmation déclarative n'est pas nécessaire.

De plus, comme cela a été dit, les langages de programmation logique font déjà exactement ce que vous voulez. On pourrait dire que le problème est la performance, mais avec le matériel et la recherche d'aujourd'hui dans ce domaine, les choses peuvent être améliorées pour être prêtes pour une utilisation en production; en fait, Prolog est utilisé pour les trucs de l'IA, mais je crois seulement par le milieu universitaire.

Il convient de noter qu'il s'applique aux langages de programmation à usage général. Pour les langues spécifiques à un domaine, les langues déclaratives sont bien meilleures; SQL est probablement le meilleur exemple.

m3th0dman
la source
3
Modélisation des données? Vous avez choisi le pire des impératifs. Les langages fonctionnels déclaratifs comme Haskell et ML excellent dans la modélisation des données. Les types de données algébriques et les types de données récursifs, par exemple, peuvent généralement être définis de manière globale sur une ou deux lignes. Bien sûr, vous avez toujours les fonctions à écrire mais leur code découle inexorablement de la définition du type et est contraint par celle-ci. Comparaison bizarre à faire.
itsbruce
1
@itsbruce La chose est que la plupart des données réelles ne sont pas facilement mappées à ADT; pensez au fonctionnement de la plupart des bases de données. En tant que Prolog - Erlang, vous avez raison, ce sont des langues différentes. J'ai mentionné que l'un est fonctionnel tandis que l'autre est logique, mais il vaut mieux que je supprime toute la comparaison.
m3th0dman
1
@ m3th0dman Une base de données n'est qu'une tonne de tuples / enregistrements. Haskell est un peu paralysé là-bas, car il manque d'enregistrements, mais il a des tuples, et ML a les deux. Et dans le cas de Haskell, la quantité de passe-partout nécessaire pour déclarer un nouveau type de données de pseudo-enregistrement est encore beaucoup plus petite que ce qu'il faut pour créer une fausse structure dans le langage OOP moyen typé statiquement. Pouvez-vous expliquer comment la plupart des données ne sont pas facilement mappées aux ADT?
Doval
1
@ m3th0dman Ah, c'est pourquoi les schémas de base de données sont définis dans un langage impératif bien adapté à la tâche. Oh, non, ce serait des DDL déclaratifs. En fait, le processus global de modélisation des données est pertinent pour l' application qui fonctionnera avec elle, les flux de données et les structures, et non le langage mettant en œuvre l'application. Parfois, ceux-ci sont déformés pour correspondre aux fonctionnalités OO d'un langage et à ce que son ORM prend en charge, mais c'est généralement une mauvaise chose, pas une fonctionnalité. Les langages déclaratifs sont mieux adaptés à l'expression du modèle de données conceptuel / logique.
itsbruce
1
@itsbruce Je ne disais pas que le paradigme procédural est meilleur que celui déclaratif pour définir les données; Je disais que le paradigme déclaratif n'est pas meilleur (ni pire) que le paradigme procédural (pour les langages à usage général). Quant à la manipulation des données, la partie déclarative de SQL n'est pas suffisante pour les applications réelles; sinon, personne n'aurait inventé et utilisé des extensions de procédure. Quant à l'article, je suis en désaccord avec l'abstrait où il contredit Brooks; il a construit ses idées à partir de projets réels alors que ces gars-là n'ont rien construit d'exceptionnel pour prouver leur théorie.
m3th0dman
0

Cela ressemblerait à quelque chose comme ça .. {(quel que soit => lire un fichier et appeler une URL) | appeler une url & lire un fichier} Cependant, ce sont des actions à exécuter, et l'état du système change en conséquence, mais ce n'est pas évident de la source.

Les déclarants peuvent décrire une machine à états finis et ses transitions. Le FSM est comme l'opposé des déclaratifs sans actions, même si la seule action est de passer de l'état à l'état suivant.

L'avantage de cette méthode est que les transitions et les actions peuvent être spécifiées par des prédicats qui s'appliquent à plusieurs transitions, plutôt qu'à une seule.

Je sais que cela semble un peu étrange, mais en 2008, j'ai écrit un générateur de programme qui utilise cette méthode, et le C ++ généré est de 2 à 15 fois plus que la source. J'ai maintenant plus de 75 000 lignes de C ++ à partir de 20 000 lignes d'entrée. Deux choses vont de pair: la cohérence et l'exhaustivité.

Cohérence: aucun prédicat qui peut être vrai en même temps ne peut impliquer des actions incohérentes, comme x = 8 et x = 9, ni différents états suivants.

Complétude: la logique de chaque transition d'état est spécifiée. Ceux-ci peuvent être difficiles à vérifier pour les systèmes avec N sous-états, avec> 2 ** N états, mais il existe des méthodes combinatoires intéressantes qui peuvent tout vérifier. En 1962, j'ai écrit la phase 1 d'un système de tri pour les machines 7070, en utilisant ce type de génération de code conditionnel et de débogage combinatoire. Sur les 8 000 lignes du genre, le nombre de bogues à partir du jour de la première version était pour toujours nul!

La phase deux du genre, 12 000 lignes, a enregistré plus de 60 erreurs au cours des deux premiers mois. Il y a beaucoup plus à dire à ce sujet, mais cela fonctionne. Si les constructeurs automobiles utilisaient cette méthode pour vérifier le firmware, nous ne verrions pas les échecs que nous voyons maintenant.

Luther Woodrum
la source
1
Cela ne répond vraiment pas à la question d'origine. Comment la cohérence et l'exhaustivité prennent-elles en compte le fait que la plupart des programmes sont encore procéduraux et non déclaratifs?
Jay Elston
Votre premier paragraphe semble être une réponse à un point de la réponse programmeurs.stackexchange.com/a/275839/67057 d' arnaud , pas à la question elle-même. Cela devrait être un commentaire là-bas (sur mon écran, votre réponse n'est plus en dessous de la sienne, pour une chose). Je pense que le reste de votre réponse est une illustration de la façon dont une petite quantité de code déclaratif pourrait générer une grande quantité de code impératif équivalent, mais ce n'est pas clair. Votre réponse a besoin d'un peu de rangement, en particulier en ce qui concerne les points saillants.
itsbruce
-3

Tout ne peut pas être représenté de manière déclarative.

Souvent, vous souhaitez explicitement contrôler le flux d'exécution

Par exemple, le pseudo-code suivant: if whatever read a file call an URL else call an URL write a file Comment le représenteriez-vous de manière déclarative?

Bien sûr, il existe probablement des moyens exotiques de le faire. Comme des monades . Mais ceux-ci sont généralement plus encombrants, compliqués et beaucoup moins intuitifs que leur partie procédurale.

Tout se résume au fait que "interagir" avec votre environnement / système n'est pas déclaratif. Tout ce qui concerne les E / S est par essence procédural. Vous devez dire exactement quand et ce qui doit arriver, et dans quel ordre.

Le déclaratif est idéal pour tout ce qui est purement lié au calcul. Comme une fonction géante, vous mettez X et vous obtenez Y. C'est super. Un exemple pour cela est HTML, l'entrée est du texte, la sortie est ce que vous voyez dans votre navigateur.

dagnelies
la source
2
Je n'achète pas ça. Pourquoi votre exemple n'est-il pas déclaratif? Est-ce le if/ else, auquel cas à quoi ressemblerait une alternative déclarative? Ce ne sont certainement pas les parties read/ write/ call, car ce sont de belles listes déclaratives de valeurs (si vous sous-entendez qu'elles sont encapsulées {...; ...}, pourquoi ne pas impliquer qu'elles sont encapsulées [..., ...]?) Bien sûr, la liste n'est que des monoides libres; beaucoup d'autres le feraient aussi. Je ne vois pas pourquoi les monades sont pertinentes ici; ce n'est qu'une API. Haskell est passé des flux -> monades pour faciliter la composition manuelle, mais les langages logiques peuvent composer des listes dans l'ordre automatiquement.
Warbo
2
-1 pour les Monades seules. 1. Ils ne sont pas vraiment exotiques (les listes et les ensembles sont des Monades et tout le monde les utilise). 2. Ils n'ont rien à voir avec le fait de forcer les choses à être faites dans une séquence spécifique (la notation Haskell do semble impérative mais ne l'est pas). Les langages déclaratifs / fonctionnels spécifient les relations et les dépendances. Si la fonction X a besoin de l'entrée Y, Y sera généré avant X. Obtenez les bonnes dépendances et la séquence appropriée d'événements se définira. Une grande partie de l'interaction est pilotée par les événements , pas dans une séquence définie. Les langages déclaratifs ne rendent pas la réaction aux événements plus difficile.
itsbruce
La paresse complique une partie de cela, mais la paresse ne fait pas partie de la définition des langages déclaratifs, dont la plupart ne l'utilisent pas. Et dans ceux qui le font, la façon de garantir l'évaluation n'a rien à voir avec les monades. Pour un exemple où un langage déclaratif est utilisé exclusivement pour l'interaction plutôt que pour le calcul abstrait, ne spécifie pas l'ordre mais s'assure que les bonnes choses se déroulent dans l'ordre, ne cherchez pas plus loin que le Puppet DSL. Ce qui a l'avantage que seules les choses nécessaires se produisent - pas quelque chose que les langues impératives rendent facile.
itsbruce
1
En plus de l'exemple de @itsbruce, la programmation réactive est considérée comme déclarative et concerne l'interaction avec l'environnement.
Maciej Piechotka