Quels sont les arguments contre l'analyse de la méthode Cthulhu?

24

On m'a confié la tâche d'implémenter un langage spécifique au domaine pour un outil qui peut devenir très important pour l'entreprise. Le langage est simple mais pas trivial, il autorise déjà les boucles imbriquées, la concaténation de chaînes, etc. et il est pratiquement sûr que d'autres constructions seront ajoutées à mesure que le projet avance.

Je sais par expérience que l'écriture d'un lexer / analyseur à la main - à moins que la grammaire soit triviale - est un processus long et sujet aux erreurs. Il me restait donc deux options: un générateur d'analyseur à la yacc ou une bibliothèque de combinateur comme Parsec. Le premier était bon aussi, mais j'ai choisi le second pour diverses raisons et j'ai implémenté la solution dans un langage fonctionnel.

Le résultat est assez spectaculaire à mes yeux, le code est très concis, élégant et lisible / fluide. Je concède que cela peut sembler un peu bizarre si vous n'avez jamais programmé autre chose que java / c #, mais cela serait vrai de tout ce qui n'est pas écrit en java / c #.

À un moment donné cependant, j'ai été littéralement agressé par un collègue. Après un rapide coup d'œil à mon écran, il a déclaré que le code était incompréhensible et que je ne devais pas réinventer l'analyse mais simplement utiliser une pile et String.Split comme tout le monde. Il a fait beaucoup de bruit, et je n'ai pas pu le convaincre, en partie parce que j'ai été pris par surprise et sans explication claire, en partie parce que son opinion était immuable (sans jeu de mots). J'ai même proposé de lui expliquer la langue, mais en vain.

Je suis certain que la discussion va refaire surface devant la direction, alors je prépare des arguments solides.

Voici les premières raisons qui me viennent à l'esprit pour éviter une solution basée sur String.Split:

  • vous avez besoin de beaucoup de ifs pour gérer des cas spéciaux et les choses deviennent rapidement incontrôlables
  • de nombreux index de tableaux codés en dur rendent la maintenance pénible
  • extrêmement difficile à gérer des choses comme un appel de fonction comme argument de méthode (ex. add ((add a, b), c)
  • très difficile de fournir des messages d'erreur significatifs en cas d'erreurs de syntaxe (très susceptible de se produire)
  • Je suis tout pour la simplicité, la clarté et pour éviter les trucs intelligents et cryptiques inutiles, mais je pense aussi que c'est une erreur de simplifier chaque partie de la base de code afin que même un hamburger flipper puisse le comprendre. C'est le même argument que j'entends pour ne pas utiliser d'interfaces, ne pas adopter la séparation des préoccupations, copier-coller du code, etc. Un minimum de compétence technique et de volonté d'apprendre est nécessaire pour travailler sur un projet logiciel après tout. (Je n'utiliserai pas cet argument car cela semblera probablement offensant, et le déclenchement d'une guerre ne va aider personne)

Quels sont vos arguments préférés contre l' analyse de la méthode Cthulhu ? *

* bien sûr, si vous pouvez me convaincre qu'il a raison, je serai aussi parfaitement heureux

smarmy53
la source
9
Cela me semble que votre collègue se porte volontaire pour faire le projet DSL pour vous!
GrandmasterB
23
"Je ne devrais pas réinventer l'analyse mais utiliser simplement une pile et String.Split comme tout le monde" - putain, ce gars devrait être content que l'ignorance ne fasse pas de mal ...
Michael Borgwardt
4
Conseillez à votre collègue de ne pas revenir à cette discussion à moins qu'il n'ait lu l'intégralité du Dragon Book et réussi un test. Sinon, il n'a pas le droit de discuter de quoi que ce soit lié à l'analyse.
SK-logic
4
désolé, qui réinventait l'analyse?
rwong
2
Je pense que ma tête explosera littéralement la prochaine fois que je verrai quelqu'un utiliser le mot "littéralement" au sens figuré.

Réponses:

33

La différence critique entre les deux approches est que celle qu'il considère comme la seule manière correcte est impérative et la vôtre est déclarative.

  • Votre approche déclare explicitement des règles, c'est-à-dire que les règles de la grammaire sont (presque) directement encodées dans votre code, et la bibliothèque d'analyseur transforme automatiquement l'entrée brute en sortie analysée, tout en prenant soin de l'état et d'autres choses difficiles à gérer. Votre code est écrit dans une seule couche d'abstraction, qui coïncide avec le domaine problématique: l'analyse. Il est raisonnable de supposer l'exactitude du parsec, ce qui signifie que la seule marge d'erreur ici est que votre définition de grammaire est incorrecte. Mais là encore, vous avez des objets de règles pleinement qualifiés et ils sont facilement testés isolément. Il convient également de noter que les bibliothèques d'analyseurs matures sont livrées avec une caractéristique importante: le rapport d'erreurs. Une récupération d'erreur décente lorsque l'analyse s'est mal passée n'est pas anodine. Pour preuve, j'invoque PHP parse error, unexpected T_PAAMAYIM_NEKUDOTAYIM: D

  • Son approche manipule les chaînes, maintient explicitement l'état et lève manuellement l'entrée brute en entrée analysée. Vous devez tout écrire vous-même, y compris les rapports d'erreur. Et quand quelque chose tourne mal, vous êtes totalement perdu.

L'ironie consiste en ce que la justesse d'un analyseur écrit avec votre approche est relativement facilement prouvée. Dans son cas, c'est presque impossible.

Il existe deux façons de construire une conception logicielle: l'une consiste à la rendre si simple qu'il n'y a manifestement aucune lacune, et l'autre consiste à la rendre si compliquée qu'il n'y a pas de lacune évidente. La première méthode est bien plus difficile.

CAR Hoare

Votre approche est la plus simple. Tout ce qu'il empêche, c'est d'élargir un peu son horizon. Le résultat de son approche sera toujours alambiqué, quelle que soit la largeur de votre horizon.
Pour être honnête, il me semble que le gars n'est qu'un imbécile ignorant, qui souffre du syndrome du blub , assez arrogant pour supposer que vous avez tort et vous crier dessus , s'il ne vous comprend pas.

Mais au final, la question est: qui va devoir le maintenir? Si c'est vous, c'est votre appel, peu importe ce que quelqu'un dit. Si ça doit être lui, alors il n'y a que deux possibilités: trouver un moyen de lui faire comprendre la bibliothèque d'analyseur ou écrire un analyseur impératif pour lui. Je vous suggère de le générer à partir de votre structure d'analyseur: D

back2dos
la source
Excellente explication de la différence entre les deux approches.
smarmy53
6
Vous êtes apparemment lié aux TVTropes pour les programmeurs. Au revoir après-midi ...
Izkata
10

Une grammaire d'expression syntaxique (telle que l'approche de l'analyseur Packrat) ou un combinateur d'analyseur ne réinvente pas l'analyse. Ce sont des techniques bien établies dans le monde de la programmation fonctionnelle et, entre de bonnes mains, elles peuvent être plus lisibles que les alternatives. J'ai vu une démonstration assez convaincante du PEG en C # il y a quelques années qui en ferait en fait mon outil de premier recours pour des grammaires relativement simples.

Si vous avez une solution élégante utilisant des combinateurs d'analyseur ou un PEG, cela devrait être une vente relativement facile: elle est assez extensible, généralement relativement facile à lire une fois que vous avez surmonté votre peur de la programmation fonctionnelle, et est parfois plus facile à lire qu'un générateur d'analyseur typique offrent des outils, bien que cela dépende beaucoup de la grammaire et du niveau d'expérience que vous avez avec l'un ou l'autre ensemble d'outils. Il est également assez facile d'écrire des tests pour. Bien sûr, il y a des ambiguïtés grammaticales qui peuvent entraîner des performances d'analyse assez horribles dans les pires scénarios (ou beaucoup de consommation de mémoire avec Packrat), mais le cas moyen est assez décent et en fait certaines ambiguïtés grammaticales sont mieux gérées avec PEG que LALR, comme Je rappelle.

L'utilisation de Split et d'une pile fonctionne avec des grammaires plus simples qu'un PEG ou peut prendre en charge, mais il est fort probable qu'au fil du temps, vous réinventerez mal la descente récursive, ou vous aurez un ensemble de comportements floconneux que vous banderez - aide à la soumission au prix d'un code extrêmement non structuré. Si vous n'avez que des règles de tokenisation simples, ce n'est probablement pas si mal, mais à mesure que vous ajoutez de la complexité, ce sera probablement la solution la moins maintenable. J'atteindrais plutôt un générateur d'analyseur.

Personnellement, ma première inclination quand j'ai besoin de construire un DSL serait d'utiliser quelque chose comme Boo (.Net) ou Groovy (JVM), car j'obtiens toute la force d'un langage de programmation existant et une personnalisation incroyable en construisant des macros et des ajustements simples au pipeline du compilateur, sans avoir à implémenter les trucs fastidieux que je finirais par faire si je partais de zéro (boucles, variables, modèle d'objet, etc.). Si j'étais dans un magasin faisant du développement Ruby ou Lisp, j'utiliserais simplement les idiomes qui ont du sens (métaprogrammation, etc.)

Mais je soupçonne que votre vrai problème concerne la culture ou les ego. Êtes-vous sûr que votre collègue n'aurait pas paniqué si vous aviez utilisé Antlr ou Flex / Bison? Je soupçonne que «plaider» pour votre solution pourrait être une bataille perdue; vous devrez peut-être passer plus de temps à adopter une approche plus douce qui utilise des techniques de recherche de consensus plutôt que de faire appel à votre autorité de gestion locale. La programmation en binôme et la démonstration de la rapidité avec laquelle vous pouvez apporter des ajustements à la grammaire sans sacrifier la maintenabilité, et faire un brownbag pour expliquer la technique, son histoire, etc., peuvent aller plus loin que 10 puces et un "Q&A grossier" à certains réunion de confrontation.

JasonTrue
la source
9

Je ne connais pas bien les algorithmes d'analyse syntaxique et autres, mais je pense que la preuve du pudding se trouve dans l'alimentation. Donc, si tout le reste échoue, vous pouvez lui proposer d'implémenter l'analyseur à sa façon. ensuite

  • comparer le temps investi dans l'une ou l'autre des solutions,
  • exécuter les deux solutions à travers un test d'acceptation complet pour voir lequel a le moins de bogues, et
  • demandez à un juge indépendant de comparer le code résultant en taille et en clarté au vôtre.

Pour que les tests soient vraiment équitables, vous souhaiterez peut-être que les deux solutions implémentent la même API et utilisent un banc d'essai commun (ou un cadre de test unitaire connu de vous deux). Vous pouvez tous les deux écrire n'importe quel nombre et type de cas de tests fonctionnels et vous assurer que sa propre solution passe tous. Et bien sûr, idéalement, aucun de vous ne devrait avoir accès à la mise en œuvre de l'autre avant la date limite. Le test décisif serait alors de contre-tester les deux solutions à l'aide de la suite de tests développée par l' autre développeur.

Péter Török
la source
C'est une bonne idée! Il serait également facile d'utiliser un cadre de tests unitaires communs.
smarmy53
1
+1 pour avoir fait travailler la version partagée par le collègue ... L'OP a été chargé de la créer, c'est donc lui qui va probablement devoir la soutenir - pas le collègue. Le simple fait de le lui suggérer en plus de son autre travail pourrait suffire à lui faire perdre la tête.
Izkata
7

Vous avez posé cette question comme si vous aviez une question technique, mais comme vous le saviez probablement déjà, il n'y a pas de question technique ici. Votre approche est largement supérieure au piratage de quelque chose au niveau du personnage.

Le vrai problème est que votre collègue (probablement plus expérimenté) n'est pas en sécurité et se sent menacé par vos connaissances. Vous ne le persuaderez pas avec des arguments techniques ; cela le rendra plus défensif. Au lieu de cela, vous devrez trouver un moyen d'atténuer ses peurs. Je ne peux pas offrir beaucoup de suggestions, mais vous pourriez essayer de montrer une grande estime pour sa connaissance du code hérité.

Enfin, si votre manager est d'accord avec ses arguments techniques spécieux et rejette votre solution, alors je pense que vous allez devoir chercher un autre poste. De toute évidence, vous seriez plus précieux et plus apprécié dans une organisation plus sophistiquée.

Kevin Cline
la source
Vous avez raison, je savais déjà que mon approche est supérieure, mais je n'ai pas réussi à fournir une bonne explication convaincante - c'est l'information technique que je recherche. D'accord, le côté "interaction humaine" du problème est aussi important que le côté technique (sinon plus).
smarmy53
4

Je serai bref:

Analyser la méthode Cthulhu est difficile. C'est l'argument le plus simple et le plus convaincant contre cela.

Il peut faire l'affaire pour les langues simples; disons, les langues régulières. Ce ne sera probablement pas plus facile qu'une expression régulière.

Il peut également faire l'affaire pour des langues un peu plus complexes.

Cependant, j'aimerais voir un analyseur Cthulhu pour n'importe quelle langue avec imbrication, ou simplement "significativement avec état" - des expressions mathématiques, ou votre exemple (appels de fonction imbriqués).

Imaginez ce qui se passerait si quelqu'un essayait de cthulhu un analyseur pour un tel langage (sans contexte non trivial). Pourvu qu'il soit assez intelligent pour écrire un analyseur correct, je parierais que lors du codage, il "découvrirait" d'abord la tokenisation, puis l'analyse de descente récursive - sous une forme ou une autre.

Après cela, la chose est simple: "Hé, regardez, vous avez écrit quelque chose qui s'appelle un analyseur de descente récursif! Savez-vous qu'il peut être généré automatiquement à partir d'une description grammaticale simple, tout comme les expressions régulières?


Pour faire court:
la seule chose qui puisse empêcher quelqu'un d'utiliser l'approche civilisée est son ignorance de celle-ci.

Kos
la source
1

Peut-être que travailler sur une bonne sémantique DSL est également important (la syntaxe est importante, mais aussi la sémantique). Si vous n'êtes pas familier avec ces questions, je vous suggère de lire quelques livres, comme Programming Language Pragmatics (par M.Scott) et Christian Queinnec. Lisp en petits morceaux . Cambridge University Press, 1996.

La lecture d'articles récents dans les conférences DSL, par exemple DSL2011 devrait également aider.

Concevoir et implémenter un langage spécifique au domaine est difficile (et la plupart du temps, ce n'est pas l' analyse!).

Je ne comprends pas vraiment ce que vous voulez dire en analysant la voie Cthulhu ; Je suppose que vous voulez juste analyser d'une manière quelque peu bizarre.

Basile Starynkevitch
la source
De bons liens. Quant à Cthulhu, désolé, j'ai oublié le lien. C'est une référence à un article classique de codinghorror: codinghorror.com/blog/2009/11/parsing-html-the-cthulhu-way.html . J'ai mis à jour le message d'origine.
smarmy53