portaldacalheta.pt
  • Principal
  • Gestion De L'ingénierie
  • Conseils Et Outils
  • Rise Of Remote
  • Agile
Back-End

La programmation déclarative existe-t-elle vraiment?



La programmation déclarative est actuellement le paradigme dominant d'un ensemble de domaines divers et étendus tels que: les bases de données, les modèles et la gestion de la configuration.

En peu de mots, programmation déclarative consiste à dire à un programme ce Quoi avoir à faire au lieu de dire Comment devrais-je le faire. En pratique, cette approche consiste à fournir un langage spécifique au domaine (DSL). Langage spécifique au domaine ) pour exprimer quoi Quoi l'utilisateur les veut et les protège des architectures ou constructions de bas niveau (boucles, conditionnelles, affectations) qui matérialisent l'état final souhaité.



Bien que ce paradigme soit une grande amélioration par rapport à l'approche impérative qu'il a remplacée, je pourrais dire que la programmation déclarative a des limitations significatives, limitations que je prévois d'explorer dans cet article. De plus, je propose une approche à deux volets qui englobe les avantages de la programmation déclarative, tout en surmontant ses limites.



CAVEAT : Cet article a été rédigé à la suite d'une lutte personnelle de plusieurs années avec les outils déclaratifs. Beaucoup de choses qu'il a fait valoir n'ont pas été pleinement prouvées et certaines sont présentées comme une valeur nominale. Une critique appropriée de la programmation déclarative prendrait beaucoup de temps, d'efforts et je devrais revenir en arrière et utiliser plusieurs de ces outils, mais mon cœur ne serait pas dans l'effort. Le but de cet article est de partager mes pensées avec vous, pas de tracas, je vais juste montrer ce qui a fonctionné pour moi. Si vous avez eu du mal avec des hauts et des bas avec des outils de programmation déclaratifs, vous pouvez trouver du réconfort et des alternatives. Et si vous aimez ce paradigme et ses outils, ne faites pas trop attention à moi.



Si la programmation déclarative fonctionne pour vous Je n'ai pas le droit de te dire le contraire .

Vous pouvez détester ou aimer la programmation déclarative, mais vous ne pouvez pas vous permettre de l



Vous pouvez détester ou aimer la programmation déclarative, mais vous ne pouvez pas vous permettre de l'ignorer. Tweet

Les mérites de la programmation déclarative

Avant d'explorer les limites de la programmation déclarative, je trouve nécessaire d'en comprendre les mérites.

Sans aucun doute, l'outil de programmation déclarative le plus efficace est la base de données relationnelle (RDB). base de données relationnelle ). Il pourrait même être considéré comme le premier outil déclaratif. Quoi qu'il en soit, RDB a deux propriétés que vous considérez comme l'archétype de la programmation déclarative:



qu'est-ce que le mood board dans la mode
  • Un langage spécifique au domaine (DSL) : l'interface universelle des bases de données relationnelles est la suivante DSL: langage de requête structuré , mieux connu sous le nom de SQL.
  • DSL masque les couches de bas niveau à l'utilisateur : depuis que Edgar F. Codd a publié son article sur RDB Il est devenu assez clair que le but de ce modèle est de dissocier les requêtes souhaitées des boucles, index et chemins sous-jacents qui les implémentent.

Avant que RDB n'existe, la plupart des systèmes de base de données étaient accessibles via un code impératif, qui dépend incroyablement des détails de bas niveau tels que les enregistrements, les index et les chemins physiques vers les données elles-mêmes. Étant donné que ces éléments changent après un certain temps, le code peut cesser de fonctionner en raison d'un changement sous-jacent dans la structure des données. Le code qui vous reste en conséquence est difficile à écrire, à déboguer, à lire et à maintenir. Très probablement, le code était très long, plein de très longues conditions, de répétitions et peut-être de quelques bogues qui n'étaient pas perceptibles mais dépendaient de l'état.

Si tel était le cas, les RDB ont fourni un très grand saut de productivité pour les développeurs de systèmes. Désormais, au lieu d'avoir des milliers de lignes de code impératif, vous pouvez avoir un schéma de données défini et aussi des centaines (voire quelques) de requêtes. En conséquence, les applications doivent uniquement traiter une représentation abstraite, significative et durable des données; et en l'interconnectant via un langage de requête puissant et en même temps simple. Le RDB a probablement contribué à accroître la productivité des programmeurs, ainsi que des entreprises qui les employaient, d'un ordre de grandeur.

Quels sont les avantages les plus courants de la programmation déclarative?

Les partisans de la programmation déclarative sont toujours prompts à mentionner ses avantages. Cependant, même ces gens admettent que cela s

Les partisans de la programmation déclarative sont toujours prompts à mentionner ses avantages. Cependant, même ces gens admettent que cela s'accompagne de certains compromis. Tweet
  1. Lisibilité / utilisabilité : un DSL est généralement plus proche d'une langue naturelle (comme l'anglais ou l'espagnol) que d'un pseudocode et est donc plus facile à lire et à apprendre par des personnes qui ne sont pas des programmeurs.
  2. Concision : La plupart des tablatures sont extraites par DSL, laissant ainsi moins de lignes pour faire le même travail.
  3. Réutilisation : il est plus facile de créer un code qui peut être utilisé à des fins différentes; quelque chose que tout le monde sait est extrêmement difficile lors de l'utilisation de constructions impératives.
  4. Idempotencia : vous pouvez travailler avec états finaux et laissez le programme faire le reste. Par exemple, dans une opération de bouleverser vous pouvez insérer une ligne si elle n'est pas là ou vous pouvez la modifier si elle est déjà là, ceci au lieu d'écrire du code qui s'occupe des deux cas.
  5. Erreur de récupération : Il est facile d'identifier une construction qui s'arrêtera à la première erreur, plutôt que d'avoir à ajouter des listes d'erreurs pour chaque erreur possible. (Si vous avez déjà écrit 3 rappels ou rappels imbriqué dans node.js pour que vous sachiez de quoi il a parlé.)
  6. Transparence référentielle Bien que cet avantage soit davantage associé à la programmation fonctionnelle, la vérité est qu'il est valable pour toute approche qui minimise la manipulation manuelle de l'état et est basée sur les effets secondaires.
  7. Commutativité - la possibilité d'exprimer un état final sans spécifier l'ordre réel dans lequel il sera mis en œuvre.

Bien que les avantages mentionnés ci-dessus soient très courants quand on parle de programmation déclarative, je vais les résumer en deux qualités qui serviront de principes directeurs pour proposer une approche alternative. Bien que les avantages ci-dessus soient tous couramment cités de la programmation déclarative, je voudrais les condenser en deux qualités, qui serviront de principes directeurs lorsque je proposerai une approche alternative.

  1. Couche de haut niveau adaptée à un domaine spécifique - La programmation déclarative crée une couche de niveau supérieur en utilisant les informations du domaine auquel elle s'applique. Certainement si nous utilisons une base de données, c'est parce que nous voulons un ensemble d'opérations pour gérer les données. La plupart des 7 avantages mentionnés ci-dessus proviennent de la création d'une couche de haut niveau spécialement conçue pour un problème de domaine.
  2. Poka-joug (infaillible) - Une couche de haut niveau adaptée à un domaine cache les détails impératifs de l'implémentation. Ce qui signifie que vous ferez moins d'erreurs car les détails de bas niveau du système ne sont pas accessibles. Cette limitation supprime de nombreux cours des erreurs dans votre code.

Deux problèmes avec la programmation déclarative

Dans les deux sections suivantes, je discuterai de deux des principaux problèmes de la programmation déclarative: séparation Oui manque de déploiement . Chaque examen a besoin d'un épouvantail et c'est pourquoi j'utiliserai un modèle de système HTML comme exemple exact des lacunes de la programmation déclarative.

Le problème des séparations DSL

Imaginez que vous ayez besoin de rédiger une application Web avec un nombre de visites non négligeable. L'encodage de ces visites dans un ensemble de fichiers HTML n'est pas une option, car de nombreux composants de ces pages changent.

La solution la plus rapide, qui serait de générer du HTML via des chaînes de concaténation, vous semble en fait horrible et vous commencerez immédiatement à chercher une alternative. La solution standard consiste à utiliser un système de modèles. Bien qu'il existe différents types de systèmes de modèles, nous mettrons leurs différences de côté pour cette discussion pour le moment. Nous pouvons les considérer comme similaires en ce qui concerne les systèmes de modèles pour fournir une alternative au codage de concaténation de chaînes HTML, en utilisant des conditions et des boucles. Tout comme les RDB sont apparus comme une alternative à la liaison de boucle via des enregistrements de données.

Supposons que nous choisissions le système de modèle standard; Vous y trouverez trois sources de frictions, que je nommerai par ordre croissant en fonction de leur importance. La première serait que le modèle doit résider dans un fichier distinct de votre code. Étant donné que le système de modèles utilise un DSL, la syntaxe est différente et ne peut donc pas être dans le même fichier. Dans les projets simples, où le nombre de fichiers n'est pas élevé, la nécessité de séparer les fichiers modèles est deux ou trois fois plus importante car elle dépasse le nombre de fichiers.

Je ferai une exception avec les modèles Rubis intégré (ERB pour son acronyme en anglais Modèles Ruby intégrés ) dû au fait que ceux ils sont intégrés au code source Ruby. Mais ce n'est pas le cas des outils inspirés d'ERB, écrits dans d'autres langues puisque ces modèles doivent être enregistrés dans des fichiers différents.

La deuxième source de friction est que DSL a sa propre syntaxe, différente de celle du langage de programmation. C'est pourquoi la modification du DSL (et même l'écriture de la vôtre) est tellement plus difficile. Pour passer sous la corde et changer d'outil, vous devrez vous renseigner sur la tokenisation et analyse (analyse), qui sont considérés comme intéressants et stimulants mais très difficiles. À mon avis, c'est un inconvénient.

Comment visualiser un DSL? Ce n

Comment visualiser un DSL? Ce n'est pas facile mais on pourrait dire qu'un DSL est une belle couche propre sur une construction bas de gamme. Tweet

Vous vous demandez peut-être: «Pourquoi diable voudriez-vous modifier votre outil? Si vous faites un projet standard, un outil bien écrit devrait fonctionner pour vous. ' Peut-être que oui, ou peut-être pas.

Un DSL n'a jamais toute la puissance d'un langage de programmation. Si c'était le cas, ce ne serait pas un DSL mais un langage de programmation complet.

Mais n'est-ce pas le but d'un DSL? Le fait que non avoir tout le contrôle d'un langage de programmation déjà disponible et ainsi nous pouvons obtenir une abstraction et également éliminer plus de sources de Bugs . Peut être. Cependant, le plus des DSL commencent simplement et commencent progressivement à incorporer un grand nombre de capacités d'un langage de programmation jusqu'à ce qu'elles soient devenir un . Les systèmes de modèles en sont l'exemple parfait. Examinons maintenant les fonctionnalités standard des systèmes de modèles et leur correspondance avec les capacités d'un langage de programmation:

  • Remplacer le texte dans un modèle : substitution de variable.
  • Répéter un modèle : boucles.
  • Évitez d'imprimer un modèle si une condition ne convient pas : conditionnels.
  • Partiel : sous-programmes.
  • Aides : sous-routines (la seule différence avec les partiels est que les assistants peuvent accéder au langage de programmation sous-jacent et vous donner la liberté de quitter DSL).

Quand ils disent qu'un DSL est limité car il est à la fois cupidité et rejette le contrôle d'un langage de programmation, ceci est directement proportionnel au fait que les fonctionnalités DSL sont mappables aux fonctionnalités d'un langage de programmation . En ce qui concerne SQL, ce n'est pas le cas car la plupart des choses offertes par SQL ne ressemblent pas à ce que vous trouveriez normalement dans un langage de programmation. Et à l'autre extrémité du spectre, nous trouvons des systèmes de modèles où pratiquement toutes les fonctionnalités font converger DSL vers DE BASE .

Revenons maintenant en arrière et contemplons ces trois sources de friction par excellence qui se résument dans le concept de séparation . Puisqu'il est séparé, un DSL doit être situé dans un fichier séparé; Il est difficile à modifier (et encore plus difficile à écrire) et (parfois mais pas toujours) il vous faut ajouter une à une, les caractéristiques dont vous avez besoin d'un vrai langage de programmation.

La séparation est un problème inhérent à tout DSL, quelle que soit sa conception.

Et maintenant un deuxième problème avec les outils déclaratifs, qui est général mais pas inhérent.

Un autre problème: le manque de déploiement conduit à la complexité

Si j'avais écrit un article il y a quelques mois, cette section aurait été appelée La plupart des outils déclaratifs sont # @! $ # @! Complexe mais je ne sais pas pourquoi . En écrivant cet article, j'ai trouvé une meilleure façon de le dire: La plupart des outils déclaratifs sont plus complexes qu'ils ne le devraient . Dans cette section, je vais expliquer pourquoi. Pour analyser la complexité d'un outil, j'aime proposer une mesure appelée marge de complexité . La marge de complexité est la différence entre résoudre un problème donné avec un outil ou le résoudre au bas niveau (code supposé simple impératif) que l'outil tente de remplacer. Lorsque la solution précédente est plus complexe que la suivante, on trouve la marge de complexité. Quand je dis plus complexe Je veux dire plus de lignes de code, un code plus difficile à lire, à modifier et à maintenir mais pas forcément tous à la fois.

Notez que nous ne comparons pas la solution de bas niveau au meilleur outil, nous comparons plutôt à aucun outil. Cela ressemble au principe médical de 'D'abord, vous ne blesserez pas' .

Signes d'un outil avec une grande marge de complexité:

  • Quelque chose qui devrait prendre quelques minutes à décrire de manière très détaillée en termes impératifs prendra des heures à coder à l'aide de l'outil, même si vous savez comment l'utiliser.
  • Vous commencez à avoir l'impression de travailler autour de l'outil plutôt qu'avec lui.
  • Vous avez du mal à résoudre un problème simple qui appartient clairement au domaine de l'outil que vous utilisez mais la meilleure réponse que vous trouvez dans Dépassement de pile la seule chose qu'il décrit est une alternative .
  • Lorsque ce problème simple pourrait être résolu par une certaine fonctionnalité (qui n'existe pas dans l'outil) et que vous voyez un problème Github dans la bibliothèque qui présente une excellente discussion de cette fonctionnalité avec divers +1 entrecoupé.
  • Comment ressentez-vous l'envie incontrôlable de poser l'outil entièrement et de tout faire vous-même dans un _ par boucle_.

Peut-être que je me laisse emporter par les émotions parce que les systèmes de modèles ne le sont pas donc compliqué mais cette marge de complexité relativement petite n'est pas un mérite de sa conception, au contraire, le domaine d'applicabilité est assez simple (rappelez-vous que nous générons du HTML). Chaque fois que la même approche est utilisée pour un domaine plus complexe (comme la gestion de la configuration), la marge de complexité pourrait rapidement transformer votre projet en bourbier.

Maintenant, il y a des exceptions où il n'est pas complètement inacceptable qu'un outil soit un peu plus complexe que le niveau bas que vous souhaitez remplacer; si l'outil donne un code plus lisible, concis et correct, cela peut en valoir la peine. C'est un problème lorsque l'outil est plusieurs fois plus complexe que le problème qu'il remplace, c'est totalement inacceptable. Brian Kernighan est célèbre pour avoir déclaré que: « Cette complexité de contrôle est l'essence même de la programmation informatique. «Si un outil ne fait qu'ajouter une complexité significative à votre projet, pourquoi l'utiliser?

Et nous nous demandons pourquoi certains outils déclaratifs sont-ils plus complexes que nécessaire? Je pense que ce serait une erreur de blâmer un mauvais design. C'est une explication tellement générale, un tel argument ad hominem envers les auteurs de ces outils que ce n'est pas juste. Il doit y avoir une autre explication, une explication plus précise et plus éclairée.

Et maintenant un petit Origami! Un outil avec une interface de haut niveau pour un bas niveau abstrait doit afficher le niveau haut depuis le bas.

Et maintenant un petit Origami! Un outil avec une interface de haut niveau pour un bas niveau abstrait doit afficher le niveau haut depuis le bas. Tweet

Mon argument est que chaque outil qui offre une interface de haut niveau pour extraire un doit déployer ce niveau supérieur à partir du bas. Le concept de déployer figurait dans l'œuvre de Christopher Alexander: 'The Nature of Order' ou La nature de l'ordre - en particulier le volume II. Il est (désespérément) hors de la portée de cet article (sans parler de ma compréhension) de résumer les implications de ce travail monumental sur la conception de logiciels; même si je crois que dans les années à venir, son impact sera énorme. Il est également hors de la portée de cet article de fournir une définition détaillée des processus de déploiement. Mais j'utiliserai le concept de forme heuristique .

Un processus de déploiement consiste à créer une structure sans nier celle qui existe. À chaque étape, chaque changement (ou différenciation, pour citer les termes d'Alexandre) reste en harmonie avec toute structure précédente lorsque la structure précédente est simplement une séquence cristallisée de changements passés.

La chose la plus intéressante est que Unix c'est un excellent exemple de déroulement d'un niveau supérieur à partir d'un niveau inférieur. Sous Unix, deux fonctionnalités complexes du système d'exploitation, les jobs batch et les coroutines (pipes) ne sont que des extensions de commandes de base. Pour certaines décisions de conception, comme faire de tout un flux d'octets, le Shell a programme utilisateur et les Les fichiers d'E / S sont standard , Unix est capable de fournir ces fonctionnalités aussi sophistiquées que la moindre complexité. Pour surprendre pourquoi ces exemples d'affichage sont excellents, je vais citer quelques extraits d'un Article de 1979 De Dennis Ritchie, l'un des auteurs Unix:

À propos des travaux par lots :

… Le nouveau schéma de processus a instantanément rendu certaines fonctionnalités précieuses faciles à mettre en œuvre; par exemple, un processus séparé (avec &) et une utilisation récursive du shell comme commande. La plupart des systèmes doivent fournir une sorte de aplicación de trabajo en lote une commande d'interprétation spéciale et une capacité pour les fichiers autres que ceux utilisés de manière interactive.

À propos des co-routines :

L'avantage des tubes Unix est précisément qu'ils sont construits à partir des mêmes commandes constamment utilisées de manière simple.

Les pionniers Unix Dennis Ritchie et Ken Thompson ont créé une excellente démo de déploiement sur leur système d

Les pionniers Unix Dennis Ritchie et Ken Thompson ont créé une excellente démo de déploiement sur leur système d'exploitation. Et ils nous ont également sauvés d'un avenir dystopique avec uniquement Windows en option. Tweet

Cette élégance et cette simplicité, il me semble, vient d'un processus de déploiement . Les travaux par lots et les coroutines sont déployés à partir de structures précédentes (les commandes fonctionnent dans un shell utilisateur). Je pense qu'en raison de la philosophie minimaliste et des ressources limitées de l'équipe qui a créé Unix, le système a évolué latéralement et en tant que tel a pu intégrer des fonctionnalités avancées sans revenir sur les plus basiques, car il n'y avait pas assez de ressources pour le faire. de toute autre manière.

En l'absence de processus de déploiement, le niveau élevé sera considérablement plus complexe que nécessaire. Autrement dit, la complexité de la plupart des outils déclaratifs se développe à partir du fait que leur niveau le plus élevé ne se déploie pas à partir du niveau bas qu'ils tentent de remplacer.

Ce manque de déploiement Si vous omettez le néologisme, il est systématiquement justifié par la nécessité de protéger l'utilisateur du niveau bas. Cette insistance sur l'utilisation d'un poka-yoke (protégeant l'utilisateur des erreurs de bas niveau) a un coût, qui est une marge de complexité qui s'autodétruit car la complexité supplémentaire générera de nouveaux types d'erreurs. Et pour terminer, ces types d'erreurs n'ont rien à voir avec le problème du domaine mais avec l'outil lui-même. Il n'y aurait pas d'exagération si nous décrivions ces erreurs comme iatrogénie .

Les outils de modèle déclaratifs, lorsqu'ils sont appliqués à la tâche de génération de vues HTML, sont un cas archétypal d'un niveau élevé rejetant un niveau bas qu'il prétend rejeter. Ainsi que? Parce que générer une vue non triviale nécessite une logique et les systèmes de modèles, en particulier les moins logiques, contournent complètement la logique, puis essaient d'en insérer un peu quand ils pensent que personne ne la regarde.

apprendre la programmation c en ligne gratuitement

Remarque: une justification encore plus médiocre pour une marge de complexité est quand ils essaient de vendre un outil comme magique ou quelque chose qui simplement travaux ; le flou de faible niveau est censé être une bonne chose car un outil magique fonctionne toujours sans que vous sachiez pourquoi ou comment il fonctionne. D'après mon expérience, plus l'outil semble magique, plus vite il transformera l'enthousiasme en frustration.

Et qu'en est-il de la séparation des préoccupations? La vue et la logique ne devraient-elles pas rester séparées? L'erreur la plus courante est de supposer que la logique métier et la logique de présentation sont identiques. La logique métier n'a aucun sens dans un modèle, mais la logique de présentation existe quoi qu'il arrive. Le contournement de la logique de modèle met de côté la logique de présentation et la place sur le serveur, où elle doit être lourde à adapter. Je dois toute cette explication claire à Alexei Boronine, qui présente un très bon cas dans cet article .

À mon avis, plus ou moins les deux tiers du travail du modèle sont dans sa logique de présentation tandis que l'autre tiers est chargé de traiter des problèmes génériques tels que: les chaînes de concaténation, les balises fermées, les échappements de caractères spéciaux et la liste continue. C'est la nature double face de la génération de vues HTML. Les systèmes d'escouade s'occupent de la seconde mi-temps, mais ils ne font pas très bien avec la première mi-temps. Les modèles sans logique tournent le dos à ce genre de problèmes sans hésitation, vous obligeant à résoudre le problème. D'autres systèmes de modèles souffrent du fait qu'ils doivent en fait fournir un langage de programmation non trivial afin que leurs utilisateurs puissent écrire une logique de présentation.

Enfin, les outils de modèles déclaratifs souffrent des éléments suivants:

  • S'ils osaient se déployer à partir de leur problème de domaine, ils devraient fournir des moyens de générer des modèles logiques;
  • Un DSL qui fournit une logique n'est pas en fait un DSL mais un langage de programmation. Il convient de noter que d'autres domaines, tels que la gestion de la configuration, souffrent également d'un manque de «déploiement».

Je terminerai cette revue par un argument qui est logiquement déconnecté du fil de discussion qui amène cet article mais qui est très proche de son noyau sentimental: nous avons peu de temps pour apprendre. La vie est courte et en dehors de cela, nous devons travailler. Lorsque nous rencontrons nos limites, nous devons passer notre temps à apprendre de nouvelles choses qui seront utiles et serviront notre temps, même lorsque nous sommes confrontés à un changement technologique rapide. C'est pourquoi je vous conseille d'utiliser des outils qui non seulement fournissent une solution mais servent en fait le domaine auquel elle s'applique. RDB vous enseigne les données et Unix vous enseigne les concepts du système d'exploitation, mais avec des outils insatisfaisants qui ne se déploient pas, j'ai toujours eu l'impression d'apprendre les subtilités d'une solution sous-optimale tout en restant dans l'ignorance de la nature du problème à résoudre.

L'heuristique que je vous suggère de considérer maintenant est ** des outils de valeur qui éclairent votre problème de domaine, plutôt que des outils qui obscurcissent votre problème de domaine derrière des fonctionnalités présomptueuses **.

L'approche jumelle

Pour surmonter les deux problèmes de programmation déclarative que j'ai évoqués ici, je propose une double approche:

  • Utilise un langage de structure de domaine de données spécifique (ou dsDSL, pour son acronyme en anglais langage spécifique à un domaine de structure de données ) pour surmonter la séparation.
  • Créez un niveau élevé qui se déroule à partir du niveau inférieur pour surmonter la marge de complexité.

dsDSL

Une structure de données DSL (dsDSL) est un DSL qui est construit avec les structures de données d'un langage de programmation . L'idée centrale est d'utiliser les structures de données de base dont vous disposez, telles que des chaînes, des nombres, des tableaux, des objets et des fonctions, puis de tout combiner pour créer des extraits capables de résoudre un domaine spécifique.

Nous voulons garder le contrôle sur les structures ou actions déclarées (de haut niveau) sans avoir à spécifier les modèles que ces constructions (de bas niveau) implémentent. Nous voulons combler le fossé entre DSL et notre langage de programmation afin d'être libres d'utiliser toute la puissance d'un langage de programmation chaque fois que nous en avons besoin. Ce n'est pas seulement possible mais une approche directe via le dsDSL.

Si vous aviez demandé il y a un an, j'aurais pensé que le concept d'un DSDSL était idéaliste, puis j'ai réalisé que JSON est en fait un excellent exemple de cette approche! Un objet JSON analysé se compose de structures de données qui représentent de manière déclarative les entrées de données afin de tirer parti de DSL et de faciliter également l'analyse et la gestion à partir d'un langage de programmation. (Il peut y avoir d'autres dsDSL dans le monde mais je n'en ai même pas vu. Si vous en connaissez, j'aimerais que vous les partagiez dans la section commentaires.)

Comme JSON, un dsDSL possède les attributs suivants:

  1. Il se compose d'un petit ensemble de fonctions: JSON a deux fonctions principales parse et stringify.
  2. Ses fonctions les plus courantes ont des arguments complexes et récursifs: un JSON analysé est un tableau ou un objet qui contient généralement d'autres tableaux ou objets.
  3. Les entrées de ces fonctions constituent des représentations spécifiques: JSON a un schéma de validation explicite et strictement appliqué qui peut différencier les structures valides des structures invalides.
  4. Les entrées et les sorties de ces fonctions peuvent être conservées et générées par un langage de programmation sans avoir besoin d'une syntaxe distincte.

Mais dsDSL va au-delà de JSON à bien des égards. Créons un dsDSL pour générer du HTML en utilisant Javascript. Ensuite, je parlerai du problème de savoir si cette approche peut être étendue à d'autres langages (SPOILER: cela peut certainement être fait en Ruby et Python mais probablement pas en C).

HTML est un langage de balisage composé de etiquetas Délimité par des chevrons (< et >). Ces balises peuvent être du contenu et des attributs facultatifs. Les attributs sont simplement une liste d'attributs clés / d'estimation et le contenu peut être du texte ou d'autres balises. Les attributs et le contenu sont facultatifs pour n'importe quelle balise. Je simplifie tout cela mais c'est plutôt bien.

Une façon très simple de représenter une balise HTML dans un dsDSL est d'utiliser un tableau avec trois éléments: - Balise: une chaîne. - Attributs: un objet (de type clé / estimation simple) ou sin definir (si aucun attribut n'est requis). - Contenu: une chaîne (texte), un tableau (une autre étiquette) ou sin definir (s'il n'y a pas de contenu).

Par exemple, Index peut s'écrire ['a', {href: 'views'}, 'Index'].

Si nous voulons insérer cet élément d'ancrage dans un div avec links de classe, on peut écrire: ['div', {class: 'links'}, ['a', {href: 'views'}, 'Index']].

Pour lister plusieurs balises html au même niveau, nous pourrions les mettre dans un tableau:

[ ['h1', '!Hola!'], ['a', {href: 'views'}, 'Index'] ]

Le même principe peut être appliqué pour créer plusieurs balises dans une seule balise:

['body', [ ['h1', '!Hola!'], ['a', {href: 'views'}, 'Index'] ]]

Bien entendu, ce dsDSL ne nous sera pas d'une grande utilité si nous ne générons pas de HTML à partir de celui-ci. Nous avons besoin d'une fonction generar ce qui obligera notre dsDSL à créer une chaîne avec HTML. C'est pourquoi si nous exécutons generar (['a', {href: 'views'}, 'Index']), nous obtiendrons la chaîne Index .

L'idée de tout DSL est de spécifier des constructions avec une certaine structure qui seront ensuite passées à une fonction. Dans ce cas, la structure qui fait le dsDSL est ce tableau qui a un à trois éléments; ces matrices ont une structure spécifique. Si generar pour valider réellement votre entrée (et il est important et facile de valider complètement votre entrée car ces règles de validation sont l'analogie précise d'une syntaxe DSL), cela vous indiquera exactement où vous avez mal saisi votre entrée. Après un certain temps, vous commencerez à reconnaître ce qui rend une structure valide dans un dsDSL, et cette structure sera très suggestive de ce qui est généré sous-jacent.

Quels sont les avantages d'un DSL par rapport à un DSL?

  • Un dsDSL fait partie intégrante de votre code. Cela permet d'avoir moins de nombres de lignes, de fichiers et une réduction de tout en général.
  • Les dsDSL sont facile à analyser (et donc plus facile à mettre en œuvre et à modifier). Analyse il s'agit simplement de parcourir les éléments d'un tableau ou d'un objet. De la même manière, les dsDSL sont relativement faciles à concevoir car au lieu de créer une nouvelle syntaxe (quelque chose que tout le monde détesterait), vous pouvez vous en tenir à la syntaxe de votre langage de programmation (quelque chose qui est également détesté mais au moins vous savez déjà ce que c'est ).
  • Un dsDSL a tout le contrôle d'un langage de programmation. Cela signifie qu'un dsDSL, lorsqu'il est utilisé correctement, présente les avantages d'un outil de haut niveau ainsi que d'un outil de bas niveau.

Maintenant, nous avons le dernier argument qui est assez fort, c'est pourquoi je vais le souligner dans cette section. Qu'est-ce que je veux dire par correctement employé ? Pour voir comment cela fonctionne, considérons un exemple où nous voulons créer une table pour afficher les informations d'un tableau appelé Maintenant, la dernière revendication est forte, je vais donc passer le reste de cette section DATA.

var DATA = [ {id: 1, descripción: 'Producto 1', precio: 20, onSale: verdadero, categorías: ['a']}, {id: 2, descripción: 'Producto 2', precio: 60, onSale: falso, categorías: ['b']}, {id: 3, descripción: 'Producto 3', precio: 120, onSale: falso, categorías: ['a', 'c']}, {id: 4, descripción: 'Producto 4', precio: 45, onSale: verdadero, categorías: ['a', 'b']} ]

Dans une vraie application DATA il sera généré dynamiquement à partir d'une requête de base de données.

Nous avons également une variable FILTER qu'une fois initialisé, ce sera un tableau avec des catégories que nous voulons afficher.

Nous voulons que notre table fasse ceci:

  • Afficher les en-têtes de tableau.
  • Pour chaque produit, les champs seront affichés: description, prix et catégories.
  • N'imprimez pas le champ id mais ajoutez-le comme attribut id pour chaque ligne. VERSION ALTERNATIVE: Ajouter un attribut id pour chaque élément tr.
  • Placer une classe onSale si le produit est en vente ( en soldes ).
  • Triez les produits par ordre décroissant par rapport au prix.
  • Filtrer certains produits par catégorie. Si FILTER est un tableau vide, nous afficherons tous les produits. Et sinon, nous montrerons les produits où la catégorie de produit est contenue dans FILTER.

Nous pouvons créer la présentation logique qui correspond à cette exigence en ~ 20 lignes de code:

function drawTable (DATA, FILTER) { var printableFields = ['description', 'price', 'categories']; DATA.sort (function (a, b) {return a.price - b.price}); return ['table', [ ['tr', dale.do (printableFields, function (field) { return ['th', field]; })], dale.do (DATA, function (product) { var matches = (! FILTER || FILTER.length === 0) || dale.stop (product.categories, true, function (category) { return FILTER.indexOf (category) !== -1; }); return matches === false ? [] : ['tr', { id: product.id, class: product.onSale ? 'onsale' : undefined }, dale.do (printableFields, function (field) { return ['td', product [field]]; })]; }) ]]; }

Je suis conscient que ce n'est pas un exemple clair, mais il représente une vue assez simple des 4 fonctions de stockage persistantes de base, également connues sous le nom de CRUEL . Toute application Web non triviale aura des vues plus complexes que cela.

Voyons maintenant ce que fait ce code. Tout d'abord, définissez une fonction, drawTable pour contenir la présentation logique du dessin de la table des produits. Cette fonction reçoit DATA et FILTER en tant que paramètres, c'est pourquoi il peut être utilisé par différents ensembles de données et filtres. drawTable Il a un double rôle, être partiel et aidant.

var drawTable = function (DATA, FILTER) {

La variable interne, printableFields, est le seul endroit où vous devez spécifier quels champs sont imprimables, pour éviter les répétitions et les incohérences face à l'évolution des exigences.

var printableFields = ['description', 'price', 'categories'];

Ensuite, nous classons les DATA selon le prix de leurs produits. Gardez à l'esprit que certains critères de classification différents et plus complexes seraient plus simples à mettre en œuvre, puisque nous avons tout le langage de programmation à notre disposition.

DATA.sort (function (a, b) {return a.price - b.price});

Ici, nous retournons un objet littéral; un tableau qui contient une 'table' comme premier élément et son contenu comme second. C'est la représentation dsDSL de

nous voulons créer.

return ['table', [

Maintenant, nous créons une ligne avec les en-têtes de tableau. Pour créer votre contenu, nous utilisons aller de l'avant qui est une fonction comme Array.map , mais cela fonctionne aussi pour les objets. Répétons printableFields et générez des en-têtes de tableau pour chacun d'eux:

['tr', dale.do (printableFields, function (field) { return ['th', field]; })],

Vous pouvez voir que nous venons d'implémenter l'itération, la main-d'œuvre de génération HTML, et qu'il n'était pas nécessaire d'avoir des versions DLS; nous avons juste besoin d'une fonction pour parcourir une structure de données et renvoyer le dsDSL. Une fonction native ou implémentée par l'utilisateur similaire aurait eu le même effet.

Vous devez maintenant parcourir les produits contenus dans DATA.

dale.do (DATA, function (product) {

Nous vérifions si ce produit est rejeté par FILTER. Si FILTER est vide, nous pouvons imprimer le produit. Si FILTER n'est pas vide, nous allons parcourir les catégories de produits jusqu'à ce que nous en trouvions une qui se trouve dans FILTER. Nous faisons cela en utilisant dale.stop .

var matches = (! FILTER || FILTER.length === 0) || dale.stop (product.categories, true, function (category) { return FILTER.indexOf (category) !== -1; });

Notez la complexité du conditionnel; il est adapté à nos besoins et nous avons une totale liberté de l'exprimer, car nous sommes dans un langage de programmation et non DSL.

Si le coincidencia est falsa, un tableau vide est renvoyé (nous n'imprimons donc pas ce produit). Sinon, a

avec son ID et sa classe respectifs, tout comme nous parcourons printableFields pour sans aucun doute imprimer les champs.

return matches === false ? [] : ['tr', { id: product.id, class: product.onSale ? 'onsale' : undefined }, dale.do (printableFields, function (field) { return ['td', product [field]];

Bien sûr, nous fermons tout ce que nous ouvrons. La syntaxe est amusante, non?

})]; }) ]]; }

Maintenant, comment intégrer ce tableau dans un contexte plus large? Nous écrivons une fonction appelée drawAll qui invoquera toutes les fonctions générées par les vues. En dehors de drawTable, on pourrait aussi avoir drawHeader, drawFooter et d'autres fonctions comparables, toutes ces dsDSLs retournera .

var drawAll = function () { return generate ([ drawHeader (), drawTable (DATA, FILTER), drawFooter () ]); }

Si vous n'aimez pas l'apparence du code ci-dessus, rien de ce que je dis ne vous convaincra. C'est dsDSL à son meilleur . Vous pouvez à ce stade arrêter de lire l'article (et aussi, laisser un commentaire diabolique, car vous avez gagné le droit de le faire si vous en êtes arrivé là!). Mais sérieusement, si le code ci-dessus n'a pas l'air sophistiqué, rien dans cet article ne le sera.

Pour ceux qui me suivent encore, je voudrais revenir à la déclaration liminaire de cette section, qui est ce que un dsDSL présente les avantages des niveaux hauts et bas :

  • La avantage de bas niveau il s'agit d'écrire des codes quand on veut, de se débarrasser de la camisole de force DSL.

  • La avantage de haut niveau consiste à utiliser des littéraux qui représentent ce que nous voulons déclarer et à laisser les fonctions de l'outil le convertir dans l'état final souhaité (dans ce cas, une séquence avec HTML).

Mais en quoi est-ce différent d'un code purement impératif? Je pense que l'élégance de l'approche dsDSL se résume au fait que le code écrit de cette manière se compose principalement d'expressions plutôt que de déclarations . Pour être plus précis, tout code qui utilise un dsDSL est à peu près composé de:

  • Littéraux qui correspondent aux structures de niveau inférieur.
  • Invocations de fonction ou lambdas dans ces structures littérales qui renvoient des structures de la même classe.

Le code composé principalement d'expressions, qui encapsule la plupart des déclarations dans des fonctions, est extrêmement court, car tous les modèles répétitifs peuvent être facilement abstraits. Vous pouvez écrire du code arbitraire, tant que ce code renvoie un littéral conforme à une forme non arbitraire très spécifique.

Une autre caractéristique de dsDSL (qui ne peut pas être explorée beaucoup pour l'instant) est la possibilité d'utiliser des types pour augmenter l'attractivité et la succulence des structures littérales. Dans un prochain article, je soulignerai ce point.

Serait-ce pour créer un dsDSL sans JavaScript, le seul vrai langage? Oui, je pense que c'est possible, tant que la langue prend en charge:

  • Littéraux pour: tableaux, objets (tableaux associatifs), invocations de fonctions et lambdas.
  • Détection du type d'exécution
  • Types de retour dynamiques et polymorphisme

Je pense que cela signifie que les dsDSL sont durables dans un langage dynamique moderne (c'est-à-dire: Ruby, Python, Perl, PHP) mais probablement pas en C ou Java.

Marchez d'abord, puis glissez: comment déplier le haut du bas

Dans cette section, je vais essayer de montrer un moyen de déployer un outil de premier niveau à partir de votre domaine. En bref, l'approche consiste en ces étapes

  1. Prenez deux ou quatre problèmes qui sont des représentations d'instances d'un problème de domaine. Ces problèmes devraient être réels. Afficher le niveau haut à partir du bas est un problème d'induction, c'est pourquoi vous avez besoin de données réelles pour pouvoir obtenir une solution représentative.
  2. Résolvez les problèmes sans outil de la manière la plus rapide possible.
  3. Prenez un moment pour examiner vos solutions et notez les modèles communs qu'elles ont.
  4. Trouvez les modèles de représentation (haut niveau).
  5. Trouvez les modèles de génération (bas niveau).
  6. Résolvez les mêmes problèmes avec votre cape de haut niveau et vérifiez que les solutions sont correctes.
  7. Si vous trouvez que vous pouvez facilement représenter tous les problèmes liés à vos modèles de rendu et que les modèles de construction pour chacune de ces instances produisent les implémentations correctes, vous êtes prêt à partir. Sinon, retournez à la planche à dessin.
  8. Si de nouveaux problèmes apparaissent, résolvez-les avec l'outil et corrigez-les en conséquence.
  9. L'outil doit converger de manière asymptotique vers un état final, quel que soit le nombre de problèmes qu'il résout. En d'autres termes, la complexité d'un outil doit rester constante plutôt que de croître avec le nombre de problèmes qu'il résout.

Que diable sont les motifs de rendu Oui modèles de génération ? Bonne chose que tu as demandé. Les modèles de représentation sont les modèles dans lesquels vous devriez pouvoir exprimer un problème qui appartient à un domaine qui concerne votre outil. C'est un alphabet de structures qui vous permet d'écrire n'importe quel motif que vous souhaitez exprimer dans le domaine applicable. Sur un DSL, ce serait les règles de production. Revenons maintenant à notre dsDSL pour générer un HTML.

La balise HTML humble est un bon exemple de modèles de rendu. Examinons maintenant de plus près ces modèles de base.

La balise HTML humble est un bon exemple de modèles de rendu. Examinons maintenant de plus près ces modèles de base. Tweet

Les modèles de rendu pour HTML sont les suivants:

  • Une balise: ['TAG']
  • Une balise avec les attributs: ['TAG', {attribute1: value1, attribute2: value2, ...}]
  • Une étiquette avec le contenu: ['TAG', 'CONTENTS']
  • Une balise avec les attributs et le contenu: ['TAG', {attribute1: value1, ...}, 'CONTENTS']
  • Une étiquette avec une autre étiquette à l'intérieur: ['TAG1', ['TAG2', ...]]
  • Un groupe de balises (seules ou dans d'autres balises): [['TAG1', ...], ['TAG2', ...]]
  • Selon une certaine condition, mettre une étiquette ou non: condition ? ['TAG', ...] : [] / Selon une certaine condition, mettre ou non un attribut: ['TAG', {class: condition ? 'someClass': undefined}, ...]

Ces instances peuvent être représentées avec la notation dsDSL que nous avons déterminée dans la section précédente. Et c'est tout ce dont vous avez besoin pour afficher le code HTML dont vous avez besoin. Des modèles plus sophistiqués tels qu'une itération conditionnelle à travers un objet pour générer une table pourraient être implémentés avec des fonctions qui renvoient les modèles de rendu mentionnés ci-dessus et ces modèles mappent directement aux balises HTML.

Si les modèles de rendu sont les structures utilisées pour exprimer ce que vous voulez, les modèles de rendu sont les structures que votre outil utilisera pour convertir les modèles de rendu en structures de bas niveau. Pour HTML, ce sont:

  • Validez l'entrée (en fait c'est un modèle de génération universel).
  • Ouvrir et fermer des balises (mais des balises vides comme, qui se ferment toutes seules)
  • Définit les attributs et le contenu, en échappant les caractères spéciaux (mais pas le contenu des étiquettes et)

Croyez-le ou non, ce sont les modèles que vous devez créer pour afficher une couche dsDSL qui génère du HTML: Des modèles similaires peuvent être trouvés pour générer du CSS. En réalité lith fait les deux dans environ 250 lignes de code.

qu'est-ce qu'une société llc c

Et enfin, qu'est-ce que je veux dire par ' marche puis glisse ? Lorsque nous rencontrons un problème de domaine, nous voulons utiliser un outil qui nous évite les terribles détails de ce domaine. En d'autres termes, nous voulons nous débarrasser du niveau bas sans que personne ne s'en rende compte, et le plus vite sera le mieux. L'approche «marcher, puis glisser» propose exactement le contraire: rester plus longtemps au niveau bas. Soyez ami avec leurs bizarreries et comprenez celles qui sont essentielles et celles qui peuvent être évitées lorsque des problèmes réels, variés et utiles surviennent.

Après avoir marché pendant un certain temps à un niveau bas, puis résolu des problèmes utiles, vous aurez une compréhension plus profonde de sa maîtrise. Les modèles de représentation et de génération émergeront naturellement; Ceux-ci découlent de la nature des problèmes qu'ils tentent de résoudre. Ensuite, vous pouvez écrire le code qui les utilisera. S'ils fonctionnent, vous pourrez passer à travers les problèmes où vous avez récemment dû marcher. Glisser signifie beaucoup de choses; cela implique vitesse, précision et absence de frottement. Peut-être que cette qualité se fait plus ressentir lorsque vous résolvez des problèmes avec cet outil, avez-vous le sentiment que vous entrez dans le problème ou avez-vous le sentiment de vous en sortir?

La chose la plus importante à propos d'un outil de déploiement n'est peut-être pas le fait qu'il nous évite d'avoir à gérer le bas de gamme. Au contraire, en capturant des modèles empiriques de répétition au bas niveau, un bon outil de haut niveau nous permet de bien comprendre l'applicabilité du domaine.

Un outil de déploiement ne résoudra pas seulement un problème, il vous aidera également à comprendre la structure du problème.

Alors ne fuyez pas un problème important. Parcourez-le d'abord, puis glissez-le.

En relation:Introduction à la programmation simultanée: guide du débutant

Figma vs Sketch vs Axure - Une revue basée sur les tâches

Outils Et Tutoriels

Figma vs Sketch vs Axure - Une revue basée sur les tâches
ApeeScape offre son talent de programmation d'élite à un coût réduit aux entrepreneurs en démarrage

ApeeScape offre son talent de programmation d'élite à un coût réduit aux entrepreneurs en démarrage

Autre

Articles Populaires
API de réseaux sociaux: le portail Internet vers le monde réel
API de réseaux sociaux: le portail Internet vers le monde réel
Explorer les raisons de la critique du Design Thinking
Explorer les raisons de la critique du Design Thinking
La concurrence et la tolérance aux pannes simplifiées: un didacticiel Akka avec des exemples
La concurrence et la tolérance aux pannes simplifiées: un didacticiel Akka avec des exemples
Dix fonctionnalités Kotlin pour booster le développement Android
Dix fonctionnalités Kotlin pour booster le développement Android
Soutenir l'offre technologique grâce à l'éducation STEM
Soutenir l'offre technologique grâce à l'éducation STEM
 
État de l'industrie Fintech (avec infographie)
État de l'industrie Fintech (avec infographie)
DevOps: qu'est-ce que c'est et pourquoi c'est important
DevOps: qu'est-ce que c'est et pourquoi c'est important
Scala vs Java: pourquoi devrais-je apprendre Scala?
Scala vs Java: pourquoi devrais-je apprendre Scala?
Qu'est-ce qu'un round down et comment en éviter un
Qu'est-ce qu'un round down et comment en éviter un
Création d'illustrations époustouflantes avec Sketch et Looper en un rien de temps
Création d'illustrations époustouflantes avec Sketch et Looper en un rien de temps
Articles Populaires
  • réflexion conceptuelle pour la stratégie d'entreprise
  • que pouvez-vous faire avec node.js
  • meilleures ressources javascript meilleures pratiques modèles
  • le budget d'investissement pour l'année est approuvé par le
  • que font les directeurs financiers
Catégories
  • Gestion De L'ingénierie
  • Conseils Et Outils
  • Rise Of Remote
  • Agile
  • © 2022 | Tous Les Droits Sont Réservés

    portaldacalheta.pt