W językach programowania zamknięcia są popularną i często pożądaną funkcją. Wikipedia mówi (moje podkreślenie):
W informatyce zamknięcie (...) jest funkcją wraz ze środowiskiem odniesienia dla zmiennych nielokalnych tej funkcji. Zamknięcie umożliwia funkcji dostęp do zmiennych poza jej bezpośrednim zakresem leksykalnym.
Zatem zamknięcie jest zasadniczo (anonimową?) Wartością funkcji, która może wykorzystywać zmienne poza swoim zakresem. Z mojego doświadczenia wynika, że może on uzyskiwać dostęp do zmiennych, które są objęte zakresem w punkcie definicji.
W praktyce koncepcja wydaje się rozbieżna, przynajmniej poza programowaniem funkcjonalnym. Różne języki wdrażają różne semantyki, a nawet wydaje się, że toczą się wojny o opinie. Wielu programistów wydaje się nie wiedzieć, czym są zamknięcia, postrzegając je jako niewiele więcej niż anonimowe funkcje.
Ponadto wydaje się, że istnieją poważne przeszkody przy wdrażaniu zamknięć. Najważniejsze, że Java 7 miała je zawierać, ale funkcja została przeniesiona z powrotem do przyszłej wersji.
Dlaczego zamknięcia są tak trudne (do zrozumienia i) do zrealizowania? To pytanie jest zbyt ogólne i niejasne, więc skupię się bardziej na tych powiązanych ze sobą pytaniach:
- Czy występują problemy z wyrażaniem zamknięć we wspólnych formalizmach semantycznych (mały krok, duży krok ...)?
- Czy istniejące systemy typów nie nadają się do zamknięć i nie można ich łatwo rozbudować?
- Czy problematyczne jest dostosowanie zamknięć do tradycyjnego tłumaczenia procedur opartego na stosie?
Zauważ, że pytanie dotyczy głównie języków proceduralnych, obiektowych i skryptowych. O ile mi wiadomo, języki funkcjonalne nie mają żadnych problemów.
źródło
Odpowiedzi:
Czy mogę skierować Cię na stronę wikipedii dotyczącą problemu Funarg ? Przynajmniej w taki sposób ludzie kompilatora odwoływali się do problemu implementacji zamknięcia.
Chociaż ta definicja ma sens, nie pomaga opisać problemu implementacji pierwszorzędnych funkcji w tradycyjnym języku opartym na stosie wykonawczym. Jeśli chodzi o kwestie związane z implementacją, funkcje pierwszej klasy można z grubsza podzielić na dwie klasy:
Pierwszy przypadek (obniżenie wartości) nie jest trudny do wdrożenia i można go znaleźć nawet w starszych językach proceduralnych, takich jak Algol, C i Pascal. C omija ten problem, ponieważ nie zezwala na funkcje zagnieżdżone, ale Algol i Pascal wykonują niezbędną księgowość, aby umożliwić funkcjom wewnętrznym odwoływanie się do zmiennych stosu funkcji zewnętrznej.
Drugi przypadek (funargs w górę) wymaga natomiast zapisania rekordów aktywacyjnych poza stosem, na stosie. Oznacza to, że bardzo łatwo jest przeciekać zasoby pamięci, chyba że środowisko uruchomieniowe języka zawiera moduł czyszczenia pamięci. Podczas gdy prawie wszystko jest dziś zbierane śmieci, wymaganie jednego z nich jest nadal znaczącą decyzją projektową, a jeszcze bardziej jeszcze jakiś czas temu.
Jeśli chodzi o konkretny przykład Javy, o ile dobrze pamiętam, głównym problemem nie była możliwość implementacji zamknięć, ale sposób wprowadzenia ich do języka w sposób, który nie był zbędny z istniejącymi funkcjami (jak anonimowe klasy wewnętrzne) i które nie kolidowały z istniejącymi funkcjami (jak sprawdzone wyjątki - problem, który nie jest ciekawostką do rozwiązania i o którym większość ludzi na początku nie myśli).
Mogę również pomyśleć o innych rzeczach, które sprawiają, że funkcje pierwszej klasy są mniej trywialne w implementacji, takich jak decydowanie o tym, co zrobić z „magicznymi” zmiennymi, takimi jak ta , self lub super, oraz jak wchodzić w interakcje z istniejącymi operatorami przepływu sterowania, takimi jak break i return (czy chcemy zezwalać na nielokalne zwroty, czy nie?). Ale ostatecznie popularność funkcji pierwszej klasy wydaje się wskazywać, że języki, które ich nie mają, robią to głównie ze względów historycznych lub z powodu jakiejś znaczącej decyzji projektowej na wczesnym etapie.
źródło
ref
parametr”). Jeśli osoba dzwoniąca zamknie wszystkie zainteresowane zmienne w strukturze, delegat może być w pełni statyczny, co pozwoli uniknąć potrzeby przydzielania sterty. Kompilatory nie oferują żadnej przyjemnej pomocy dla takich konstrukcji, ale Framework może je obsługiwać.Możemy spojrzeć na sposób implementacji zamknięć w C #. Skala transformacji, które wykonuje kompilator C #, wyraźnie pokazuje, że ich sposób implementacji zamknięć jest dość pracochłonny. Mogą istnieć łatwiejsze sposoby implementacji zamknięć, ale sądzę, że zespół kompilatora C # byłby tego świadomy.
Rozważmy następujący pseudo-C # (wyciąłem trochę rzeczy specyficznych dla C #):
Kompilator przekształca to w coś takiego:
(w rzeczywistości zmienna f nadal będzie tworzona, gdzie f jest „delegatem” (= wskaźnik funkcji), ale ten delegat jest nadal powiązany z obiektem theClosureObject - pozostawiłem tę część dla jasności dla tych, którzy nie są zaznajomieni z C #)
Ta transformacja jest dość masywna i trudna: rozważ zamknięcia w zamknięciach i współdziałanie zamknięć z pozostałymi funkcjami języka C #. Mogę sobie wyobrazić, że ta funkcja została wypchnięta z powrotem dla Javy, ponieważ Java 7 ma już całkiem sporo nowych funkcji.
źródło
Aby odpowiedzieć na część twojego pytania. Formalizm opisany przez Morrisetta i Harpera obejmuje dużą i małą semantykę języków polimorficznych wyższego rzędu zawierających zamknięcia. Przed nimi są artykuły przedstawiające rodzaje semantyki, których szukasz. Spójrz na przykład na maszynę SECD . Dodawanie zmiennych odwołań lub zmiennych lokalnych do tej semantyki jest proste. Nie widzę żadnych problemów technicznych w zapewnieniu takiej semantyki.
źródło