Powody usunięcia typów funkcji w Javie 8

12

Próbowałem zrozumieć, dlaczego grupa ekspertów JDK 8 Lambda Expert Group (EG) postanowiła nie włączać nowego typu funkcji do języka programowania Java.

Przeglądając listę mailingową, znalazłem wątek z dyskusją na temat usuwania typów funkcji .

Wiele stwierdzeń jest dla mnie niejednoznacznych, być może z powodu braku kontekstu, a w niektórych przypadkach z powodu mojej ograniczonej wiedzy na temat wdrażania systemów typów.

Jednak jest kilka pytań, które moim zdaniem mogę bezpiecznie sformułować na tej stronie, aby pomóc mi lepiej zrozumieć, co one oznaczają.

Wiem, że mogę zadawać pytania na liście mailingowej, ale wątek jest stary i wszystkie decyzje są już podejmowane, więc są szanse, że będę zignorowany, przede wszystkim widząc, że ci faceci już opóźniają się ze swoimi planami.

W swojej odpowiedzi na poparcie usuwania typów funkcji i poparcia użycia typów SAM Brian Goetz mówi:

Brak potwierdzenia Był długi wątek na temat tego, jak przydatne byłoby powtórzenie typów funkcji. Bez potwierdzenia typy funkcji są zapętlone.

Nie mogłem znaleźć wątku, o którym wspomina. Teraz rozumiem, że wprowadzenie typu funkcji strukturalnej może wiązać się z pewnymi komplikacjami w systemie typu Java o typie nominalnym. Nie rozumiem, jak różne są sparametryzowane typy SAM pod względem reifikacji.

Czy oboje nie podlegają tym samym problemom związanym z reifikacją? Czy ktoś rozumie, w jaki sposób typy funkcji różnią się od sparametryzowanych typów SAM pod względem reifikacji?

W innym komentarzu Goetz mówi:

Istnieją dwa podstawowe podejścia do pisania: nominalne i strukturalne. Tożsamość nominału opiera się na jego nazwie; tożsamość typu strukturalnego jest oparta na tym, z czego się składa (np. „krotka int, int” lub „funkcja od int do float”). Większość języków wybiera głównie nominalne lub głównie strukturalne; nie ma wielu języków, które z powodzeniem łączą pisanie nominalne i strukturalne, z wyjątkiem „wokół krawędzi”. Java jest prawie całkowicie nominalna (z kilkoma wyjątkami: tablice są typem strukturalnym, ale na dole zawsze jest typowy element nominalny; generyczne mają również mieszankę nominalnego i strukturalnego, a to w rzeczywistości część źródła wielu skarg ludzi na generyczne.) Przeszczepianie systemu typów strukturalnych (typów funkcji) na Javę ” System typów nominalnych oznacza nową złożoność i przypadki krawędzi. Czy korzyść z typów funkcji jest tego warta?

Ci z was, którzy mają doświadczenie we wdrażaniu systemów typu. Czy znasz jakieś przykłady tych zawiłości lub przypadkowych przypadków, o których tu wspomina?

Szczerze mówiąc, mylą mnie te zarzuty, gdy uważam, że język programowania, taki jak Scala, który jest całkowicie oparty na JVM, obsługuje typy strukturalne, takie jak funkcje i krotki, nawet przy problemach z weryfikacją podstawowej platformy.

Nie zrozum mnie źle, nie mówię, że typ funkcji powinien być lepszy niż typy SAM. Chcę tylko zrozumieć, dlaczego podjęli tę decyzję.

edalorzo
źródło
4
„Przeszczepienie strukturalnego systemu typów (typy funkcji) do nominalnego systemu typów Java oznacza nową złożoność” - najprawdopodobniej oznaczało to złożoność dla użytkowników, którzy muszą nauczyć się więcej, jak korzystać z języka . Coś takiego jak prosta i łatwa w obsłudze Java staje się złożonym i trudnym do nauczenia się C ++ , takie tam. Członkowie listy mailingowej mogliby prawdopodobnie nawiązywać do krytyki wcześniejszej „wielkiej fali ulepszeń”, jednym z wybitnych przykładów jest ssący artykuł o Javie 5 autorstwa Clintona Begina z MyBatis
gnat
3
„Prosta i łatwa w użyciu Java staje się złożonym i trudnym do nauczenia się C ++”: Niestety, jednym z problemów w głównych językach programowania jest to, że muszą zachować kompatybilność wsteczną i dlatego stają się zbyt skomplikowane. IMHO (mając wieloletnie doświadczenie zarówno w C ++, jak i Javie) jest w tym stwierdzeniu coś prawdziwego. Często mam wrażenie, że Java powinna zostać zamrożona i zamiast tego należy opracować nowy język. Ale Oracle nie może podjąć takiego ryzyka.
Giorgio
4
@edalorzo: Jak pokazali Clojure, Groovy, Scala i inne języki, możliwe jest (1) zdefiniowanie nowego języka (2) korzystanie z całego bogactwa bibliotek i narzędzi, które są już napisane w Javie bez naruszania kompatybilności wstecznej (3) Programiści Java mają swobodę wyboru, czy chcą pozostać przy Javie, przejść na inny język lub używać obu. IMO, rozszerzanie istniejącego języka na czas nieokreślony jest strategią utrzymywania klientów w taki sam sposób, w jaki firmy telefonii komórkowej muszą ciągle tworzyć nowe modele.
Giorgio
2
Jak zrozumiałem z SAMbdas w Javie , jednym z powodów jest to, że utrzymywanie zgodności niektórych bibliotek (kolekcji) z poprzednimi wersjami byłoby problematyczne.
Petr Pudlák
2
Powiązany post na SO, który prowadzi do tego innego postu autorstwa Goetza .
assylias

Odpowiedzi:

6

Podejście SAM jest w rzeczywistości nieco podobne do tego, co Scala (i C ++ 11) robią z anonimowymi funkcjami (utworzonymi za pomocą =>operatora Scali lub []()składni C ++ 11 (lambda)).

Pierwszym pytaniem, na które należy odpowiedzieć po stronie języka Java, jest to, czy typ zwracany instrukcji lambda powinien być nowym typem pierwotnym , takim jak intlub byte, lub jakimś rodzajem obiektu. W Scala, nie mają żadnych prymitywnych typów - nawet liczbą całkowitą jest obiektem klasy Int- i funkcje nie są różne, będące obiektami klasy Function1, Function2i tak dalej, w zależności od liczby argumentów funkcja przyjmuje.

C ++ 11, ruby ​​i python podobnie mają wyrażenia lambda, które zwracają obiekt, który można wywołać w sposób jawny lub niejawny. Zwrócony obiekt ma jakąś standardową metodę (np. #callKtóra może być użyta do wywołania go jako funkcja. Na przykład C ++ 11 używa std::functiontypu, który jest przeciążony, operator()tak że wywołania metody wywołania obiektu wyglądają nawet jak wywołania funkcji tekstowo. :-)

Tam, gdzie nowa propozycja dla Javy staje się nieporządna, stosuje się strukturalne pisanie, aby umożliwić przypisanie takiej metody do innego obiektu, na przykład takiego, Comparatorktóry ma jedną główną metodę o innej nazwie . Chociaż jest to pod pewnym względem niepoprawne koncepcyjnie, oznacza to, że wynikowy obiekt można przekazać do istniejących funkcji, które przyjmują obiekt do reprezentowania wywołania zwrotnego, komparatora itp. I oczekują, że będą w stanie wywołać dobrze zdefiniowany singiel metoda taka jak #comparelub #callback. Sztuczka C ++ polegająca na operator()starannym obejściu tego problemu nawet przed C ++ 11, ponieważ wszystkie takie opcje wywołania zwrotnego można było wywoływać w ten sam sposób, a zatem STL nie wymagał dostosowania, aby umożliwić użycie lambd C ++ 11 zsorti tak dalej. Java, która w przeszłości nie używała standardowego nazewnictwa dla takich obiektów (być może dlatego, że żadna sztuczka, taka jak przeciążenie operatora, nie było oczywistym jednym podejściem), nie ma tyle szczęścia, więc ten hack uniemożliwia im zmianę wielu istniejących interfejsów API .

jimwise
źródło
1
Jak na ironię, problem posiadania niezliczonej liczby różnych, niekompatybilnych typów, z których wszystkie reprezentują coś w rodzaju „funkcji podobnej do funkcji”, można by z łatwością uniknąć, dodając do biblioteki standardowe typy funkcji, niezależnie od faktycznej dosłownej składni do ich budowy. Gdyby był java.util.Function2<T1, T2, R>w Javie 1.0, nie byłoby Comparatorinterfejsu, zamiast tego wszystkie te metody wymagałyby Function2<T1, T2, int>. (Cóż, byłby cały szereg interfejsów, na przykład z Function2TT_int<T1, T2>powodu prymitywów, ale rozumiem.)
Jörg W Mittag,