Les continuations de première classe sont-elles utiles dans les langages de programmation orientés objet modernes?

10

Les continuations sont extrêmement utiles dans les langages de programmation fonctionnels (par exemple la Contmonade dans Haskell) car elles permettent une notation simple et régulière pour le code de style impératif. Ils sont également utiles dans certains anciens langages impératifs car ils peuvent être utilisés pour implémenter des fonctionnalités de langage manquantes (par exemple, les exceptions, les coroutines, les fils verts). Mais pour un langage moderne orienté objet avec prise en charge intégrée de ces fonctionnalités, quels arguments y aurait-il pour ajouter également la prise en charge des continuations de première classe (que ce soit le style délimité plus moderne resetet / shiftou le schéma call-with-current-continuation)?

Existe-t-il des arguments contre l' ajout d'un support autre que les performances et la complexité de la mise en œuvre?

Jules
la source

Réponses:

15

Laissons Eric Lippert répondre à celle-ci:

La question évidente à ce stade est: si CPS est si génial, alors pourquoi ne l'utilisons-nous pas tout le temps? Pourquoi la plupart des développeurs professionnels n'en ont-ils jamais entendu parler ou, ceux qui l'ont vu, pensent-ils comme quelque chose que seuls les programmeurs de Scheme fous font?

Tout d'abord, il est tout simplement difficile pour la plupart des gens qui ont l'habitude de penser aux sous-programmes, boucles, try-catch-enfin et ainsi de suite de raisonner sur l'utilisation des délégués pour le flux de contrôle de cette manière. Je suis en train de revoir mes notes sur CPS de CS442 en ce moment et je vois qu'en 1995 j'ai écrit un profQUOTE: "Avec les continuations, vous devez en quelque sorte vous tenir debout et vous tirer à l'envers". Le professeur Duggan (*) avait absolument raison de dire cela. Rappelons il y a quelques jours que notre minuscule petit exemple de transformation CPS de l'expression C # M (B ()? C (): D ()) impliquait quatre lambdas. Tout le monde n'est pas bon en lecture de code qui utilise des fonctions d'ordre supérieur. Il impose un lourd fardeau cognitif.

De plus: l'une des bonnes choses à avoir des instructions de flux de contrôle spécifiques intégrées dans une langue est qu'elles permettent à votre code d'exprimer clairement la signification du flux de contrôle tout en masquant les mécanismes - les piles d'appels et les adresses de retour et les listes de gestionnaires d'exceptions et les régions protégées etc. Les continuations rendent les mécanismes de contrôle des flux explicites dans la structure du code. Tout cet accent mis sur le mécanisme peut écraser le sens du code.

Dans l'article suivant , il explique comment l'asynchronie et les continuations sont exactement équivalentes, et passe en revue une démonstration de la prise d'une opération réseau synchrone simple (mais bloquante) et de la réécriture dans un style asynchrone, en veillant à couvrir tous les éléments cachés des pièges qui doivent être couverts pour bien faire les choses. Cela se transforme en un énorme gâchis de code. Son résumé à la fin:

Sainte bonté, quel désordre divin nous avons fait. Nous avons étendu deux lignes de code parfaitement clair en deux douzaines de spaghettis les plus divins que vous ayez jamais vus. Et bien sûr, il ne compile toujours pas, car les étiquettes ne sont pas dans la portée et nous avons une erreur d'affectation définitive. Nous devons toujours réécrire le code pour résoudre ces problèmes.

Rappelez-vous ce que je disais sur les avantages et les inconvénients de CPS?

  • PRO: Des flux de contrôle arbitrairement complexes et intéressants peuvent être construits à partir de pièces simples - vérifiez.
  • CON: La réification du flux de contrôle via les continuations est difficile à lire et difficile à raisonner - vérifiez.
  • CON: Le code qui représente les mécanismes du flux de contrôle submerge complètement la signification du code - vérifiez.
  • CON: La transformation du flux de contrôle de code ordinaire en CPS est le genre de chose que les compilateurs sont bons, et presque personne d'autre ne l'est - vérifiez.

Ce n'est pas un exercice intellectuel. Les vraies personnes finissent par écrire du code moralement équivalent à ce qui précède tout le temps quand elles traitent de l'asynchronie. Et, ironiquement, même si les processeurs sont devenus plus rapides et moins chers, nous passons de plus en plus de notre temps à attendre des choses qui ne sont pas liées au processeur. Dans de nombreux programmes, une grande partie du temps passé est avec le processeur fixé à zéro en attente de paquets réseau pour faire le voyage de l'Angleterre au Japon et vice-versa, ou pour que les disques tournent, ou autre chose.

Et je n'ai même pas parlé de ce qui se passe si vous voulez continuer à composer des opérations asynchrones. Supposons que vous souhaitiez faire des ArchiveDocuments une opération asynchrone qui prend un délégué. Maintenant, tout le code qui l'appelle doit également être écrit en CPS. La souillure se propage.

Il est important de bien définir la logique asynchrone , cela ne fera que l'être à l'avenir, et les outils que nous vous avons donnés vous font « vous tenir debout et vous mettre à l'envers», comme l'a judicieusement dit le professeur Duggan.

Le style de passage de continuation est puissant, oui, mais il doit y avoir un meilleur moyen de faire bon usage de ce pouvoir que le code ci-dessus.

Les deux articles et la série suivante sur une nouvelle fonctionnalité du langage C # qui déplace tout ce désordre dans le compilateur et vous permet d'écrire votre code en tant que flux de contrôle normal avec un mot-clé spécial pour marquer certaines parties comme asynchrones, valent la peine d'être lus même si vous '' re pas un développeur C #. Je ne le suis pas, mais c'était quand même une expérience très enrichissante quand je l'ai rencontrée pour la première fois.

Mason Wheeler
la source
5
Il convient de noter que si votre langage prend en charge les extensions de syntaxe comme les macros, les continuations deviennent beaucoup plus utiles car vous pouvez masquer les mécanismes de mise en œuvre de différents types de flux de contrôle intéressants à l'intérieur des extensions de syntaxe. De toute façon, comment "attendre" fonctionne de toute façon, c'est juste défini dans le langage car C # ne fournit pas les outils pour définir vous-même ce type d'extensions de syntaxe.
Jack
Bon article, bien que je note que CPS et les continuations de première classe ne sont pas exactement la même chose (CPS est ce que vous faites dans une langue sans prise en charge des continuations lorsque vous trouvez que vous en avez besoin). Il semble que C # suit exactement la même voie que celle à laquelle je pense (bien que je ne puisse pas décider si je veux automatiser la transformation des pneus en CPS ou implémenter des continuations délimitées par copie de pile à part entière).
Jules
@Jack - c'est exactement ce que j'envisage en ce moment: le langage que je conçois a une fonction macro qui pourrait prendre en charge la transformation CPS automatiquement. Mais les continuations natives complètes pourraient donner de meilleures performances ... la question est, à quelle fréquence seraient-elles utilisées dans une situation critique pour les performances?
Jules