Le continuazioni di prima classe sono utili nei moderni linguaggi di programmazione orientati agli oggetti?


10

Le continuazioni sono estremamente utili nei linguaggi di programmazione funzionale (ad esempio la Contmonade in Haskell) perché consentono una notazione semplice e regolare per il codice in stile imperativo. Sono utili anche in alcuni linguaggi imperativi più vecchi perché possono essere utilizzati per implementare funzionalità linguistiche mancanti (ad es. Eccezioni, coroutine, fili verdi). Ma per un linguaggio orientato agli oggetti moderno con supporto integrato per queste caratteristiche, quali argomenti ci sarebbe per anche aggiungendo il supporto per continuazioni di prima classe (se lo stile delimitata più moderno resete shifto schema simile call-with-current-continuation)?

Ci sono argomenti contro l' aggiunta di supporto oltre alle prestazioni e alla complessità dell'implementazione?

Risposte:


15

Lasciamo che Eric Lippert risponda a questo:

La domanda ovvia a questo punto è: se CPS è così fantastico, allora perché non lo usiamo sempre? Perché la maggior parte degli sviluppatori professionisti non ne ha mai sentito parlare o, quelli che hanno, lo pensano come qualcosa che fanno solo quei pazzi programmatori di Scheme?

Prima di tutto, è semplicemente difficile per la maggior parte delle persone che sono abituate a pensare a subroutine, loop, try-catch-finally e così via per ragionare sui delegati che vengono utilizzati per il flusso di controllo in questo modo. Sto rivedendo i miei appunti su CPS da CS442 in questo momento e vedo che nel 1995 ho scritto un profQUOTE: "Con le continuazioni devi in ​​qualche modo stare in piedi e tirarti dentro". Il professor Duggan (*) aveva assolutamente ragione nel dirlo. Ricordiamo da un paio di giorni fa che il nostro piccolo esempio di trasformazione CPS dell'espressione C # M (B ()? C (): D ()) riguardava quattro lambda. Non tutti sono bravi a leggere il codice che utilizza funzioni di ordine superiore. Impone un grande carico cognitivo.

Inoltre: una delle cose belle di avere istruzioni specifiche sul flusso di controllo incorporate in una lingua è che lasciano che il tuo codice esprima chiaramente il significato del flusso di controllo nascondendo i meccanismi: la chiamata impila e restituisce indirizzi, elenchi di gestori di eccezioni e aree protette e così via. Le continuazioni rendono espliciti i meccanismi del flusso di controllo nella struttura del codice. Tutta questa enfasi sul meccanismo può sopraffare il significato del codice.

Nel prossimo articolo , spiega come l'asincronia e le continuazioni sono esattamente equivalenti tra loro e analizza la dimostrazione di eseguire un'operazione di rete sincrona semplice (ma bloccante) e riscriverla in stile asincrono, assicurandosi di coprire tutto il nascosto Gotcha che devono essere coperti per farlo bene. Si trasforma in un enorme casino di codice. Il suo riassunto alla fine:

Santo cielo, che divino pasticcio abbiamo fatto. Abbiamo ampliato due righe di codice perfettamente chiaro in due dozzine di righe degli spaghetti più divini che tu abbia mai visto. E ovviamente non si compila nemmeno perché le etichette non rientrano nell'ambito e abbiamo un errore di assegnazione definito. Dobbiamo ancora riscrivere ulteriormente il codice per risolvere questi problemi.

Ricordi cosa stavo dicendo dei pro e dei contro di CPS?

  • PRO: Flussi di controllo arbitrariamente complessi e interessanti possono essere costruiti con parti semplici - verifica.
  • CON: La reificazione del flusso di controllo tramite continuazioni è difficile da leggere e difficile da ragionare.
  • CON: Il codice che rappresenta i meccanismi del flusso di controllo travolge completamente il significato del codice - check.
  • CON: La trasformazione del normale flusso di controllo del codice in CPS è il tipo di cosa in cui i compilatori sono bravi e quasi nessun altro lo è - controlla.

Questo non è un esercizio intellettuale. Le persone vere finiscono per scrivere codice moralmente equivalente a quanto sopra quando si occupano di asincronia. E, ironia della sorte, anche se i processori sono diventati sempre più veloci ed economici, passiamo sempre più tempo ad aspettare cose che non siano legate al processore. In molti programmi, gran parte del tempo trascorso è con il processore ancorato a zero in attesa di pacchetti di rete per fare il viaggio dall'Inghilterra al Giappone e viceversa, o per far girare i dischi, o altro.

E non ho nemmeno parlato di cosa succede se si desidera comporre ulteriormente le operazioni asincrone. Supponiamo di voler rendere ArchiveDocuments un'operazione asincrona che richiede un delegato. Ora tutto il codice che lo chiama deve essere scritto anche in CPS. La contaminazione si diffonde appena.

Ottenere la logica asincrona nel modo giusto è importante, sarà solo più importante in futuro e gli strumenti che ti abbiamo dato ti faranno "stare in testa e capovolgerti", come ha detto saggiamente il professor Duggan.

Lo stile del passaggio di continuazione è potente, sì, ma deve esserci un modo migliore per sfruttare al meglio quel potere rispetto al codice sopra.

Entrambi gli articoli e le seguenti serie su una nuova funzionalità del linguaggio C # che sposta tutto questo casino nel compilatore e ti consente di scrivere il codice come normale flusso di controllo con una parola chiave speciale per contrassegnare alcune parti come asincrone, vale la pena leggere anche se non sei uno sviluppatore C #. Non lo sono, ma è stata comunque un'esperienza illuminante quando l'ho incontrata per la prima volta.


5
Vale la pena notare che se il tuo linguaggio supporta estensioni di sintassi come le macro, le continuazioni diventano molto più utili perché puoi nascondere i meccanismi per implementare vari tipi di flusso di controllo interessante all'interno delle estensioni di sintassi. Questo è fondamentalmente il modo in cui "wait" funziona comunque, è solo definito nella lingua poiché C # non fornisce gli strumenti per definire quel tipo di estensioni di sintassi da soli.
Jack,

Un buon articolo, anche se noterò che CPS e le continuazioni di prima classe non sono esattamente la stessa cosa (CPS è ciò che fai in una lingua senza supporto per le continuazioni quando scopri di averne bisogno). Sembra che C # stia percorrendo esattamente la stessa strada a cui sto pensando (anche se non riesco a decidere se voglio automatizzare la trasformazione delle gomme in CPS o implementare continuazioni delimitate da copia in pila completa).
Jules,

@Jack - questo è esattamente quello che sto prendendo in considerazione in questo momento: il linguaggio che sto progettando ha una funzione macro che potrebbe supportare automaticamente la trasformazione CPS. Ma le continuazioni native complete potrebbero offrire prestazioni migliori ... la domanda è: con quale frequenza verrebbero utilizzate in una situazione critica per le prestazioni?
Jules,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.