Qual è un esempio di continuazione non implementata come procedura?


15

Una discussione interessante sulla distinzione tra callback e continuazioni su SO ha sollevato questa domanda. Per definizione, una continuazione è una rappresentazione astratta della logica necessaria per completare un calcolo. Nella maggior parte delle lingue questo si manifesta come una procedura a un argomento a cui si passa qualunque valore necessiti di elaborazione continua.

In un linguaggio puramente funzionale (dove tutte le funzioni sono pure e cittadini di prima classe), penso che una continuazione possa essere interamente modellata come una funzione. Dopotutto, è così che ho compreso in precedenza le continuazioni fino a questo punto. Tuttavia, il mondo è pieno di stato (sospiro ...) e quindi la definizione generale non richiede che uno stato del programma di acquisizione di continuazione - debba solo comprendere l'intento.

Per aiutare la mia comprensione, può essere fornito un esempio in un linguaggio funzionale in cui la continuazione è espressa in un modo più astratto di una funzione? So che Scheme ti permette di afferrare la continuazione corrente in un modo di prima classe (call / cc), ma anche così, sembra che alla procedura di un argomento passata a call / cc sia semplicemente data la continuazione corrente sotto forma di un'altra procedura argomento a cui la funzione call / cc'd può applicare il suo risultato.


Forse l'intersezione di continenze e defezionalizzazione come: le continuazioni possono essere convertite in strutture di dati tramite defezionalizzazione; potrebbe essere un'area interessante da guardare.
Dan D.

@DanD. hai qualche suggerimento per letteratura interessante che posso leggere? Questo argomento sembra utile.
David Cowden,

Risposte:


11

tl; dr; Il tipo è l'astrazione generale su una continuazione


Una continuazione è il tipo dei suoi input e output

La cosa più vicina a una continuazione non basata su procedure è probabilmente la monade di continuazione in Haskell in quanto espressa come un tipo, per cui molte funzioni possono essere utilizzate per interagire con il tipo per interrompere, riprendere, tornare indietro, ecc.

Puoi incapsulare quella chiusura in un tipo come il Conttipo in Haskell in cui ottieni l'astrazione della monade come "astrazione di livello superiore" e ci sono altre forme di astrazione rispetto alle continuazioni che ottieni quando guardi la continuazione come un tipo invece che semplicemente una procedura , per esempio

  • Puoi prendere due continuazioni e fare un'alternativa tra loro se il tipo segue le leggi per essere un monoide
  • È possibile astratta sopra il tipo di cambiare i tipi di ingresso o di uscita della continuazione se incapsulare la chiusura in un tipo che si attiene alle leggi di un funtore
  • Puoi applicare o decorare arbitrariamente e parzialmente la tua continuazione con funzionalità come la convalida dell'input o la conversione dell'input se incapsuli la chiusura in un tipo che segue le leggi di un agente applicativo

Chiusura vs. procedura

Alla fine della giornata hai praticamente ragione; una continuazione è una "procedura", anche se preferirei chiamarla chiusura. Spesso le continuazioni si esprimono meglio come chiusure di prima classe che hanno racchiuso un ambiente associato. In un linguaggio funzionale puro potresti dire che questo non è particolarmente ragionevole perché manchi di riferimenti; questo è vero ma puoi racchiudere i valori e la singola assegnazione rende la racchiude il valore rispetto al riferimento esattamente la stessa cosa. Questo dà origine a Haskell:

(\x -> \y -> insideYIcanAccess x (and y))

Un linguaggio che manca della capacità di racchiudere un ambiente vincolante può tecnicamente mancare di chiusure di prima classe, ma anche allora c'è un ambiente (generalmente globale) che è disponibile per la chiusura.

Quindi direi che è più preciso descrivere una continuazione come: una chiusura utilizzata in un modo particolare.


Conclusione

Alla domanda "La continuazione è implementabile in qualche modo diverso da una procedura?" No. Se non disponi di funzioni di prima classe, non puoi davvero avere delle continuazioni in quanto tali (sì, i puntatori di funzione contano come funzioni di prima classe, quindi in alternativa può bastare un accesso arbitrario alla memoria).

Ora alla domanda "Esistono modi per esprimere una continuazione in un modo più astratto di una procedura?" Esprimerlo come un tipo ti dà un'astrazione molto maggiore, permettendoti di trattare la continuazione in modi molto generali in modo da poter interagire con la continuazione in molti più modi rispetto alla semplice esecuzione.


1
Questo si generalizza a "Una continuazione può essere semplicemente qualsiasi cosa che ti permetta di ottenere il risultato del resto del programma". Dato che questo normalmente richiede un codice (il resto del programma) la maggior parte delle lingue usa le funzioni. Teoricamente puoi costruire una continuazione da qualsiasi cosa. Durante la conversione di continuazione nei miei compilatori, ho usato alberi parzialmente riempiti.
Daniel Gratzer,

1
Gli alberi parzialmente riempiti di @jozefg sono una corretta rappresentazione di un calcolo come espressione, ma alla fine della giornata il codice reale in fase di scrittura è una sorta di espressione che non può essere identificabile in modo diverso da una procedura, come la capisco. A parte questo, gli alberi parzialmente riempiti sono la rappresentazione del compilatore; la rappresentazione degli sviluppatori è prevedibilmente un'espressione computazionale normativa con la sintassi e la semantica del linguaggio, che apparirebbe al 99% degli sviluppatori come una "procedura", una "funzione" o altro. Stai pensando dal punto di vista dello sviluppatore del compilatore heh
Jimmy Hoffa,

2

Un esempio che ti potrebbe piacere sono le coroutine. Ad esempio, i Coroutine di Lua o gli iteratori / generatori di Python o C # sono simili in potenza alle continuazioni one-shot (continuazioni che ti è permesso chiamare solo una volta) ma la continuazione non è esplicitamente trasformata in una funzione. Invece, hai modi per far avanzare la coroutine fino alla prossima dichiarazione "yield".

Ad esempio, considera il seguente programma Python:

def my_iterator():
   yield 1
   yield 2
   yield 3

def main():
   it = my_iterator()
   x = it.next()
   y = it.next()
   z = it.next()
   print x + y + z

È simile al seguente programma Javascript con richiamate esplicite:

function my_iterator()
  return function(cb1){
    cb1(1, function(cb2){
      cb2(2, function(cb3){
        cb3(3, function(cb4){
          throw "stop iteration";
        });
      });
    });
  });
}

function main(){
   var getNext1 = my_iterator();
   getNext1(function(x, getNext2){
      getNext2(function(y, getNext3){
         getNext3(function(z, getNext4){
            console.log(x + y + z);
         });
      });
   });
}

L'esempio Javascript è un po 'rumoroso perché ogni passaggio deve restituire la continuazione successiva oltre a restituire il valore ceduto (nel Python tiene traccia della continuazione all'interno dell'it

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.