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ę.
źródło
Odpowiedzi:
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
int
lubbyte
, lub jakimś rodzajem obiektu. W Scala, nie mają żadnych prymitywnych typów - nawet liczbą całkowitą jest obiektem klasyInt
- i funkcje nie są różne, będące obiektami klasyFunction1
,Function2
i 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.
#call
Która może być użyta do wywołania go jako funkcja. Na przykład C ++ 11 używastd::function
typu, 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,
Comparator
któ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#compare
lub#callback
. Sztuczka C ++ polegająca naoperator()
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 zsort
i 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 .źródło
java.util.Function2<T1, T2, R>
w Javie 1.0, nie byłobyComparator
interfejsu, zamiast tego wszystkie te metody wymagałybyFunction2<T1, T2, int>
. (Cóż, byłby cały szereg interfejsów, na przykład zFunction2TT_int<T1, T2>
powodu prymitywów, ale rozumiem.)