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 .
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
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?
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.
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.
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.
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:
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.
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é:
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.
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 deaplicació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.
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:
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 **.
Pour surmonter les deux problèmes de programmation déclarative que j'ai évoqués ici, je propose une double approche:
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:
parse
et stringify
.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?
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:
id
mais ajoutez-le comme attribut id
pour chaque ligne. VERSION ALTERNATIVE: Ajouter un attribut id
pour chaque élément tr
.onSale
si le produit est en vente ( en soldes ).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 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 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 Nous vérifions si ce produit est rejeté par 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 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 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: 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: 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. 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 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. Les modèles de rendu pour HTML sont les suivants: 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: 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. 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. nous voulons créer.
return ['table', [
printableFields
et générez des en-têtes de tableau pour chacun d'eux: ['tr', dale.do (printableFields, function (field) { return ['th', field]; })],
DATA
. dale.do (DATA, function (product) {
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; });
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]];
})]; }) ]]; }
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 () ]); }
Marchez d'abord, puis glissez: comment déplier le haut du bas
['TAG']
['TAG', {attribute1: value1, attribute2: value2, ...}]
['TAG', 'CONTENTS']
['TAG', {attribute1: value1, ...}, 'CONTENTS']
['TAG1', ['TAG2', ...]]
[['TAG1', ...], ['TAG2', ...]]
condition ? ['TAG', ...] : []
/ Selon une certaine condition, mettre ou non un attribut: ['TAG', {class: condition ? 'someClass': undefined}, ...]
qu'est-ce qu'une société llc c