2009-07-23 13 views
27

Czytałem gdzieś, gdzie bogaty Hickey powiedział:Kontynuacje w Clojure

„Myślę kontynuacje może być schludny w teorii, ale nie w praktyce”

nie jestem zaznajomiony z Clojure.
1. Czy clojure ma kontynuację?
2. Jeśli nie, nie potrzebujesz kontynuacji? Widziałem wiele dobrych przykładów, szczególnie z this guy. Jaka jest alternatywa?
3. Jeśli tak, czy istnieje dokumentacja?

+1

Twój tytuł pytania nie ma wiele wspólnego z tekstem pytania. –

+0

Czy to lepiej? – unj2

Odpowiedz

26

Mówiąc o kontynuacje, trzeba rozróżnić dwa rodzaje z nich:

  • pierwszej klasy kontynuacje - kontynuacją wsparcia, które jest głęboko zintegrowany w języku (Schemat lub Ruby). Clojure nie obsługuje pierwszej klasy kontynuacji.

  • Kontynuacja przechodzenia stylu (CPS) - CPS to tylko styl kodowania i każdy język obsługujący anonimowe funkcje pozwoli na ten styl (co dotyczy również Clojure).

Przykłady:

-- Standard function 
double :: Int -> Int 
double x = 2 * x 

-- CPS-function – We pass the continuation explicitly 
doubleCPS :: Int -> (Int -> res) -> res 
doubleCPS x cont = cont (2 * x) 
; Call 
print (double 2) 

; Call CPS: Continue execution with specified anonymous function 
double 2 (\res -> print res) 

Czytaj continuation na Wikipedia.

Nie sądzę, aby kontynuacja była konieczna dla dobrego języka, ale szczególnie wysokiej klasy kontynuacje i CPS w językach funkcjonalnych, takich jak Haskell, mogą być całkiem przydatne (intelligent backtracking example).

+9

Odnośnie drugiego z tych dwóch punktów, czy w celu ogólnym nie byłoby konieczne korzystanie z funkcji optymalizacji wywołań końcowych? Zastanów się nad standardową funkcją, którą otoczysz blokiem "while (! Aborted)", aby była powtarzana, aż do przerwania. Czy ekwiwalentem funkcji stylu CPS nie byłoby samo wysłanie jej jako kontynuacji? W ten sposób uzyskasz nieograniczoną rekurencję i wymagana będzie optymalizacja połączeń. Po prostu głośno tu myślę, więc proszę, powiedz mi, czy to ma sens :-) – harms

13

Czy kontynuacja jest niezbędną funkcją w języku?

Nie. Wiele języków nie ma kontynuacji.

Jeśli nie, czy nie potrzebujesz kontynuacji? Widziałem wiele dobrych przykładów, szczególnie od tego gościa. Jaka jest alternatywa?

Wezwanie stos

+7

+1 za stos wywoławczy – Dario

7

Powszechnym zastosowaniem kontynuacje jest we wdrażaniu struktur sterowania dla: powrocie z funkcji, łamiąc z pętli, wyjątek obsługi itp większości języków (jak Java, C++ itp) zapewniają te funkcje jako część podstawowego języka. Niektóre języki nie (np. Scheme). Zamiast tego, te języki eksponują kontynuacje jako obiekty pierwszej klasy i pozwalają programistom zdefiniować nowe struktury kontrolne. Schemat powinien być postrzegany jako zestaw narzędzi do programowania, a nie jako kompletny język.

W Clojure prawie nigdy nie musimy korzystać z kontynuacji bezpośrednio, ponieważ prawie wszystkie struktury kontrolne są dostarczane przez kombinację język/VM. Mimo to kontynuacja pierwszej klasy może być potężnym narzędziem w rękach kompetentnego programisty. Zwłaszcza w Scheme, kontynuacje są lepsze niż odpowiedniki w innych językach (jak para setjmp/longjmp w C). This artykuł zawiera więcej szczegółów na ten temat.

Przy okazji, będzie interesujące dowiedzieć się, w jaki sposób Rich Hickey uzasadnia swoją opinię na temat kontynuacji. Jakieś linki do tego?

+1

Jakie są cechy, które musisz mieć, aby język był "pełny"? – unj2

+2

To powinno być "pełne", a nie "pełne". Kiedy powiedziałem "kompletny", nie miałem na myśli, że Turing jest kompletny. Miałem na myśli cechy, których ludzie zwykle oczekują od "nowoczesnych" języków. Na przykład obsługa wyjątków za pomocą try-catch. Schemat tego nie zapewnia, ale przy użyciu najwyższej klasy kontynuacji można zaimplementować własny mechanizm obsługi wyjątków. –

+8

Good Lispers pisze programy, rozwijając język w kierunku problemu.Innymi słowy, przekształcasz Lispa w język, który najlepiej nadaje się do rozwiązania problemu. To duża różnica między Lisps i innymi językami. Paul Graham wyjaśnia to szczegółowo w swojej książce "On Lisp". Nie wiem, kim jesteś i co robisz, ale przypuszczam, że nie napisałeś żadnego znaczącego oprogramowania w Lisp. W przeciwnym razie nie musiałbym tego wyjaśniać. Prowadzę własny program i codziennie go używam, aby rozwiązywać rzeczywiste problemy. –

0

Cóż ... Clojure na -> realizuje to, czego po ... Ale z makra zamiast

+0

Myślę, że jest to poprawna odpowiedź. Wynik jednego kroku zostaje przekazany na wejście kolejnego kroku. Efekt jest mechanizmem typu kontynuacji, uzyskanym przez przepisanie kodu – zcaudate

+0

Nie sądzę, że ten "->" naprawdę wychwytuje w ogóle to, co PO jest po. W najlepszym przypadku '->' jest podobne do modad tożsamości. Teraz, gdybyś mógł zmienić '->' zamiast dowolnie dowolnej monady, zgodziłbym się z tobą, niż '->' byłby wystarczający do wyrażenia każdego obliczenia, które używa kontynuacji (możesz po prostu użyć Monady Kontynuacji). Ale '->', jak zaimplementowano w Clojure, nie daje żadnego sposobu na zawieszenie i wznowienie obliczeń. Pierwszej klasy kontynuacje dają ci pełną kontrolę nad częściami obliczeń i kiedy są uruchamiane. Tego zupełnie nie ma przy pomocy '->'. –

+0

To prawda, co powiedzieliście o kontroli nad egzekucją. w każdym razie ... to może cię zainteresować http://docs.caudate.me/hara/hara-concurrent-procedure.html – zcaudate

5

Streszczenie Kontynuacje

Kontynuacje są abstrakcyjne pojęcie, które są używane do opisu semantyki przepływu sterowania. W tym sensie oba istnieją i nie istnieją (pamiętaj, że są abstrakcyjne) w dowolnym języku, który oferuje operatory kontroli (jak każdy inny język Turinga), w taki sam sposób, w jaki istnieją obie liczby (jako obiekty abstrakcyjne) i nie istnieją (jako namacalne byty).

Kontynuacje opisują efekty kontrolne, takie jak wywoływanie/zwracanie funkcji, obsługa wyjątków, a nawet próby. Dobrze napisany język będzie, między innymi, zaprojektowany z abstrakcjami opartymi na kontynuacjach (np. Wyjątkach). (Oznacza to, że dobrze napisany język będzie składał się z operatorów kontrolnych, którzy zostali zaprojektowani z myślą o kontynuacji.) Jest oczywiście całkowicie uzasadnione, aby język mógł ujawniać kontynuacje jako abstrakcja kontroli, umożliwiając użytkownikom tworzenie własne abstrakcje na wierzchu.)

First Class Kontynuacje

Jeśli pojęcie kontynuacji jest reified jako obiekt pierwszej klasy w języku, to mamy narzędzie, na którym wszystkie rodzaje efektów sterowania można wybudowany. Na przykład, jeśli język ma kontynuacje pierwszej klasy, ale nie wyjątki, możemy tworzyć wyjątki oprócz kontynuacji.

Problemy z pierwszej klasy Kontynuacje

Podczas kontynuacją pierwszej klasy są potężne i użyteczne narzędzie w wielu przypadkach istnieją również pewne wady wystawiając je w języku:

  • Różne abstrakcji budowane kontynuacja może spowodować nieoczekiwane/nieintuicyjne zachowanie po złożeniu. Na przykład blok finally może zostać pominięty, jeśli użyję kontynuacji do przerwania obliczeń.
  • Jeśli bieżąca kontynuacja może być wymagana w dowolnym czasie, wówczas czas uruchamiania języka musi być skonstruowany w taki sposób, aby w danym momencie możliwe było wygenerowanie reprezentacji struktury danych bieżącej kontynuacji. To powoduje pewien stopień obciążenia dla funkcji, która na dobre lub na gorsze jest często uważana za "egzotyczną". Jeśli język jest hostowany (taki jak Clojure jest hostowany w JVM), to reprezentacja musi być w stanie zmieścić się w ramach przewidzianych przez platformę hostingową. Mogą istnieć również inne funkcje, które język chciałby utrzymać (np. C interop), które ograniczają przestrzeń rozwiązania. Takie problemy zwiększają potencjał "niedopasowania impedancji" i mogą poważnie skomplikować rozwój rozwiązania opartego na wydajności.

Dodanie pierwszej klasy kontynuacje do języka

Through metaprogramowanie, możliwe jest dodanie wsparcia dla kontynuacji pierwszej klasy na języku. Zasadniczo podejście to polega na transformacji kodu do stylu przekazywania kontynuacji (CPS), w którym bieżąca kontynuacja jest przekazywana jako jawny argument do każdej funkcji.

Na przykład biblioteka Davida Nolena delimc implementuje rozdzielone kontynuacje części programu Clojure za pomocą serii przekształceń makr. W podobnym duchu napisałem: pulley.cps, który jest makro kompilatorem, który przekształca kod w CPS, wraz z biblioteką wykonawczą do obsługi większej liczby podstawowych funkcji Clojure (takich jak obsługa wyjątków) oraz współdziałania z natywnym kodem Clojure.

Jednym z problemów związanych z tym podejściem jest sposób radzenia sobie z granicami między natywnym (Clojure) i przekształconym (CPS) kodem. W szczególności, ponieważ nie możesz uchwycić kontynuacji kodu natywnego, musisz albo zabronić (lub w jakiś sposób ograniczyć) współdziałanie z językiem podstawowym, albo obciążyć użytkownika, upewniając się, że kontekst pozwoli dowolnej kontynuacji, którą chcą uchwycić, aby faktycznie zostać złapanym.

pulley.cps zmierza w kierunku tego drugiego, chociaż niektóre próby zostały podjęte, aby umożliwić użytkownikowi zarządzanie tym. Na przykład, możliwe jest zablokowanie kodu CPS w celu wywołania natywnego kodu. Ponadto zapewniono mechanizm dostarczania wersji CPS istniejących funkcji natywnych.

W języku o wystarczająco silnym systemie typów (takim jak Haskell), możliwe jest zastosowanie systemu typu do enkapsulacji obliczeń, które mogą wykorzystywać operacje kontrolne (tj. Kontynuacje) z funkcjonalnie czystego kodu.

Podsumowanie

Mamy teraz informacje niezbędne, aby bezpośrednio odbierać swoje trzy pytania:

  1. Clojure nie obsługuje pierwszej klasy kontynuacje spowodowane względami praktycznymi.
  2. Wszystkie języki są zbudowane na kontynuacjach w sensie teoretycznym, ale niewiele języków eksponuje kontynuacje jako pierwszorzędne obiekty. Jednak możliwe jest dodanie kontynuacji do dowolnego języka poprzez, na przykład, przekształcenie w CPS.
  3. Sprawdź dokumentację dla delimc i/lub pulley.cps.
+0

Minęło prawie siedem lat, ale w końcu mamy poprawną i dokładną odpowiedź na to pytanie. Doskonały! Powinno to stać się zaakceptowaną odpowiedzią. –

+0

@BenKovitz Dzięki. Zastanawiałem się, czy udzielić odpowiedzi, biorąc pod uwagę wiek pytania. Twój komentarz sprawia, że ​​cieszę się, że tak zrobiłem. –