Interesująca dyskusja na temat rozróżnienia między wywołaniami zwrotnymi a kontynuacjami w SO spowodowała, że pytanie to się pojawiło. Z definicji kontynuacja jest abstrakcyjną reprezentacją logiki potrzebnej do ukończenia obliczeń. W większości języków manifestuje się to jako procedura jednopargumentowa, do której przekazujesz dowolną wartość wymagającą dalszego przetwarzania.
W czysto funkcjonalnym języku (gdzie wszystkie funkcje są czystymi obywatelami pierwszej klasy), pomyślałbym, że kontynuacja mogłaby być całkowicie modelowana jako funkcja. W końcu tak właśnie rozumiałem kontynuacje do tego momentu. Jednak świat jest pełen stanu (westchnienie ...), więc ogólna definicja nie wymaga, aby stan programu przechwytywania kontynuacji - wymagał jedynie zamiaru.
Aby pomóc mi zrozumieć, czy można podać przykład w języku funkcjonalnym, w którym kontynuacja jest wyrażona w sposób bardziej abstrakcyjny niż funkcja? Wiem, że Schemat pozwala uchwycić bieżącą kontynuację w pierwszorzędny sposób (call / cc), ale mimo to wydaje się, że procedura jednego argumentu przekazana do call / cc jest po prostu dana bieżącej kontynuacji w postaci innej procedura argumentu, do której funkcja call / cc'd może zastosować swój wynik.
źródło
Odpowiedzi:
tl; dr; Typ jest nadrzędnym abstrakcji nad kontynuacją
Kontynuacją jest rodzaj danych wejściowych i wyjściowych
Najbardziej zbliżoną do kontynuacji nieopartej na procedurach jest prawdopodobnie monada kontynuacji w Haskell, ponieważ jest wyrażona jako typ, dla którego można użyć wielu funkcji do interakcji z typem w celu przerwania, wznowienia, cofnięcia i innych.
Możesz zamknąć to zamknięcie w typ, taki jak
Cont
typ w Haskell, w którym otrzymujesz abstrakcję monady jako „abstrakcję wyższego poziomu”, a istnieją inne formy abstrakcji nad kontynuacjami, które otrzymasz, gdy spojrzysz na kontynuację jako typ zamiast po prostu procedura , na przykładZamknięcie a procedura
Pod koniec dnia masz rację; kontynuacja jest „procedurą”, choć wolałbym nazywać ją „zamknięciem”. Często kontynuacje są najlepiej wyrażane jako zamknięcia pierwszej klasy, które otaczają ograniczone środowisko. W czysto funkcjonalnym języku można powiedzieć, że nie jest to szczególnie uzasadnione, ponieważ brakuje referencji; jest to prawda, ale można zawrzeć wartości, a pojedyncze przypisanie sprawia, że zawarcie wartości względem odwołania jest dokładnie takie samo. To powoduje w Haskell:
Język, który nie ma możliwości zamknięcia wiążącego środowiska, może technicznie nie mieć zamknięć pierwszej klasy, ale nawet wtedy istnieje pewne środowisko (ogólnie globalne), które jest dostępne dla zamknięcia.
Powiedziałbym więc, że dokładniej jest opisać kontynuację jako: Zamknięcie jest używane w określony sposób.
Wniosek
Na pytanie „Czy kontynuację można wdrożyć w jakikolwiek inny sposób niż procedurę?” Nie. Jeśli nie masz funkcji pierwszej klasy , tak naprawdę nie możesz mieć kontynuacji jako takiej (tak wskaźniki funkcji liczą się jako funkcje pierwszej klasy, więc alternatywnie wystarczający może być dowolny dostęp do pamięci).
Teraz pytanie: „Czy są jakieś sposoby na wyrażenie kontynuacji w sposób bardziej abstrakcyjny niż procedura?” Wyrażenie go jako typu daje znacznie większą abstrakcję, pozwalając traktować kontynuację w bardzo ogólny sposób, dzięki czemu możesz wchodzić w interakcję z kontynuacją na wiele innych sposobów niż tylko jej wykonanie.
źródło
Jednym z przykładów, które mogą ci się spodobać, są coroutines. Na przykład Coroutines z Lua lub iteratory / generatory z Python lub C # mają moc podobną do kontynuacji one-shot (kontynuacje, które można wywołać tylko raz), ale kontynuacja nie jest jawnie przekształcona w funkcję. Zamiast tego masz sposoby, aby przesunąć koronę do następnej instrukcji „wydajności”.
Rozważmy na przykład następujący program w języku Python:
Jest podobny do następującego programu Javascript z wyraźnymi wywołaniami zwrotnymi:
Przykład Javascript jest trochę głośny, ponieważ każdy krok musi zwrócić następną kontynuację oprócz zwracanej wartości (w Pythonie śledzi kontynuację wewnątrz ite
źródło