Jakie są zalety i wady podejścia C #, Java i Scala do zamknięć / Lambdas /…?

30

Zastanawiam się, jakie są techniczne różnice w implementacji między C # i Scalą oraz jak oba rozwiązania porównują się z pomysłami i obawami dotyczącymi implementacji wyrażonymi w e-mailu Peek Past lambda Briana Goetza, wysłanym na listę mailingową projektu Lambda (JSR 335) ?

Z e-maila:

Badaliśmy drogę „może lambdas powinny być po prostu klasami wewnętrznymi, to byłoby naprawdę proste”, ale ostatecznie doszliśmy do wniosku, że „funkcje są lepszym kierunkiem dla przyszłości języka”.

i dalej:

Ujęcie lambdas-are-objects świata w konflikcie z tą możliwą przyszłością. Widok świata na funkcje lambda nie jest taki, a zachowanie tej elastyczności jest jednym z punktów przemawiających za tym, aby nie obciążać lambdów nawet wyglądem obiektowym.

Wniosek:

Funkcje Lambdas-are-otwiera drzwi. Lambda-are-obiekty zamyka je.
Wolimy, aby te drzwi były otwarte.

A niektóre komentarze od osób w wątku Reddit mówią:

Właściwie wysłałem e-mail do Neala Gaftera na ten temat i ku mojemu ograniczonemu zrozumieniu jego wyjaśnienia C #, a obecny projekt Java jest dość podobny, ponieważ Delegaci są w rzeczywistości obiektami, a nie typami funkcji. Wygląda na to, że wierzy, że Java powinna uczyć się z wad lambda C # i unikać ich (podobnie jak C # nauczył się z wad Javy i unikał ich na początku).

Dlaczego podejście „Lambdas-are-funkcje” zapewnia więcej możliwości w przyszłości niż „Lambdas-are-objects”? Czy ktoś może wyjaśnić, jakie różnice istnieją i jak wpłynęłyby na sposób pisania kodu?

Widząc, że rzeczy w Scali „po prostu działają”, ciągle myślę, że coś mi brakuje w podejściach przyjętych / zaproponowanych w C # / Java (8), prawdopodobnie jest to związane z obawami o kompatybilność wsteczną?

soc
źródło
1
To jest interesujące. Biorąc pod uwagę, że system typu Java nie ma na razie pojęcia funkcji, oznaczałoby to, że trzeba to wprowadzić jako pierwsze. Możliwe, że język stanie się zbyt skomplikowany.
Ingo
Czy funkcje nie powinny mieć instancji jakiegoś interfejsu? Co jest w nich takiego specjalnego?
soc
Lisp / Scheme / Clojure może modelować obiekty, podczas gdy Java / C # nie może modelować dowolnego zagnieżdżenia funkcji. Jednak wydaje się, że Python ma to, co najlepsze w obu światach.
Job

Odpowiedzi:

15

Myślę, że dyskusja na temat obiektów vs. funkcji to czerwony śledź. Jeśli pytanie brzmi: „Czy lambda jest funkcją czy przedmiotem?” odpowiedź powinna brzmieć tak .

Taki jest sens funkcji pierwszej klasy: nie są one traktowane inaczej niż jakikolwiek inny typ. Java już w większości ignoruje różnice między typami Object i pierwotnymi (a Scala robi to jeszcze lepiej), więc to, czy lambda jest podklasą Object, czy jest nowym typem pierwotnym, czy czymś innym, nie jest tak naprawdę ważne dla języka. Ważne jest to, że możesz umieszczać swoje lambdy w kolekcjach, przekazywać je do wywołania, wywoływać je i robić cokolwiek innego, co chcesz zrobić z przedmiotem lub metodą.

Scala osiąga to poprzez użycie obiektu otoki, który ma wywoływaną metodę, applyktórą można wywołać za pomocą just (), dzięki czemu wygląda jak wywołanie metody. Działa to wspaniale; przez większość czasu nie musisz nawet dbać o to, czy masz metodę, czy wywołujesz zastosowanie obiektu funkcji.

Rex Kerr
źródło
9

Z tego, co rozumiem, chodzi bardziej o to, jak lambdas są rozpatrywane na poziomie języka podstawowego. Jak mówi e-mail,

Można by pomyśleć, że obecny projekt jest ściśle powiązany ze skrzynką obiektową dla lambd - typów SAM - co czyni je skutecznie obiektami. Ale zostało to starannie ukryte przed powierzchnią, aby umożliwić nam rozważenie „nagich” lambdów w przyszłości, rozważenie innych kontekstów konwersji dla lambdów lub ściślejszą integrację lambdów w konstrukcjach kontrolnych. Nie robimy tego teraz i nie mamy nawet konkretnego planu, ale możliwość zrobienia tego w przyszłości jest kluczową częścią projektu.

Myślę, że to całkiem ładnie podsumowuje twoje pytanie. Jeśli oświadczysz, że „lambdy są obiektami”, to są one po prostu przedmiotem konkretnej klasy i utkniesz z tym. Z drugiej strony, jeśli zadeklarujesz „lambdy są funkcjami”, to semantycznie masz znacznie bogatsze pole gry. Java 1.7 może kompilować je do obiektów, więc są praktycznie identyczne w tym momencie.

Ale Java 1.8 lub 1.9 może wprowadzać zmiany w języku (takie jak zmienione typy strukturalne), niż umożliwiać korzystanie z funkcji w znacznie bardziej elastyczny sposób. Gdyby „lambdas były obiektami”, zmiany te nie byłyby kompatybilne wstecz i trzeba by wprowadzić zupełnie nową koncepcję, aby nie złamać istniejącego kodu użytkownika. Ale jeśli javacpo prostu cicho przekształcał lambdy w obiekty za kulisami, nowy javacmoże je przekonwertować na wszystko, co chce, o ile semantyka nadal obowiązuje.

Andrzej Doyle
źródło
lamdas w C #! = od delegatów. Kompilator ma opcję kompilowania ich w drzewach delgatów lub wyrażeń. oba są rzeczywiście wykresy obiektu, ale nie są one skompilowane do o „szczególnej klasy” ani nawet klasy specjalnym drzewie dziedziczenia
Rune FS
Jeśli dobrze rozumiem, to skompilowanie lambda do wewnętrznej klasy obiektu, który jest z nią powiązany, wiąże go z tym obiektem, więc jeśli w przyszłości Java miałaby wprowadzić funkcje wyższego rzędu (funkcje na tym samym poziomie obiektów), to ty nie mógł korzystać z implementacji klasy wewnętrznej i pojawiłaby się niespójność. Nie sądzę, że Java będzie miała wkrótce funkcje wyższego rzędu.
mwolfetech
@mwolfetech: O ile mi wiadomo, są już planowane dla Java 8 z metodami obrońcy. Chcą, aby dodać takie rzeczy jak foreach, map, filterdo kolekcji.
soc
@soc Dobra uwaga, trudno jest nadążyć za liczbą JSR i tym, czy faktycznie ukończą i stworzą docelową wersję JDK. Wygląda na to, że metody obrońcy mogą być częścią tego samego JSR. W odniesieniu do tego JSR uznałem, że post Briana Goetza o stanie Lambdy był pomocny - wyjaśnia typy SAM i bieżące przemyślenia dotyczące implementacji wyrażeń lambda.
mwolfetech
„aktualne myśli” mhhh, czy ten post z 2010 roku nie jest już nieaktualny?
soc
5

Przeważnie nie chce po prostu angażować się zbyt szybko w coś. Nie widzę żadnego powodu poza tym, który przedstawił, ale to naprawdę bardzo silny powód.

Nie lubię typów funkcji - uwielbiam typy funkcji - ale te typy funkcji źle walczyły z istniejącym aspektem systemu typów Java, usuwanie. Wymazane typy funkcji są najgorsze z obu światów.

Rozważ te metody w Scali Regex:

def replaceAllIn (target: CharSequence, replacer: (Match)  String): String
def replaceSomeIn (target: CharSequence, replacer: (Match)  Option[String]): String

Byłoby łatwiej, gdyby były to po prostu:

def replace (target: CharSequence, replacer: (Match)  String): String
def replace (target: CharSequence, replacer: (Match)  Option[String]): String

Niestety nie jest to możliwe, ponieważ te dwie funkcje są usuwane identycznie . Ale w porządku, te funkcje wykonują nieco inne rzeczy, więc inna nazwa może być wystarczająca.

Jednak a Matchjest czymś bardzo przydatnym, ale przez większość czasu chcesz dopasować Stringlub listę podgrup. Chcielibyśmy mieć to:

def replaceAllIn (target: CharSequence, replacer: (String)  String): String
def replaceAllIn (target: CharSequence, replacer: (Seq[String])  String): String
def replaceAllIn (target: CharSequence, replacer: (Match)  String): String

Niemożliwe z powodu usunięcia. Wybrałem ten konkretny przykład, ponieważ byłem bezpośrednio z nim związany, ale widziałem co najmniej pół tuzina pytań na temat Przepełnienia stosu, gdzie ludzie pytają, dlaczego przeciążenie jest nielegalne, spowodowane właśnie tym problemem.

I weź pod uwagę, że dopasowywanie wzorców Scali i Java instanceofsą skutecznie bezużyteczne z powodu skasowania.

Myślę, że opóźnianie typów funkcji jest sprawiedliwe, dopóki problem nie zostanie rozwiązany. W końcu jest wiele języków, które mają to w JVM i nie jest tak, że Java jest językiem szybko ewoluującym.

Daniel C. Sobral
źródło
Cóż, jeśli wymazanie tekstu jest jedynym problemem, jaki mają, to co zamierzają zrobić? Złamanie każdego kodu na tej planecie było już wykluczone dla Javy 5
soc
@soc Jestem tak zadowolony, że nie jestem w ich buty! Ale to było Słońce, to jest Wyrocznia. Firma Oracle nigdy nie miała skrupułów w dokonywaniu przełomowych zmian między głównymi - a nawet drobnymi - wersjami swojego głównego produktu.
Daniel C. Sobral,
Może powinni zmienić nazwę języka i zacząć opracowywać nowy język, rezygnując z kompatybilności wstecznej. Tak więc zyskałbyś zalety nowego języka bez potrzeby zabawy ze starszym i dobrze ugruntowanym językiem. W końcu to właśnie zrobili Scala i Clojure.
Giorgio
@Giorgio To byłoby trochę bezcelowe. Cóż, niektórzy ludzie, którzy są odpowiedzialni za to, co Java jest dziś zrobił, ale, jak pan powiedział, że alternatywy. Ale ludzie odpowiedzialni za Javę muszą poprawić samą Javę - w końcu są za nią odpowiedzialni . Jedyną alternatywą jest po prostu porzucenie języka Java i rezygnacja ze wszystkich korzyści wynikających z kontrolowania go.
Daniel C. Sobral
@Daniel C. Sobral: IMO język programowania przypomina trochę oprogramowanie: na początku może być czysty i dobrze zaprojektowany, ale im więcej się nim bawi, tym bardziej robi się bałagan. Myślę więc, że w interesie programistów byłoby zamrożenie Javy jako języka i skoncentrowanie się na (1) tworzeniu nowych bibliotek lub ulepszaniu istniejących, (2) ulepszaniu JVM (jeśli to możliwe), (3) rozwijaniu nowych języków . Ale jak powiedziałeś, są ludzie odpowiedzialni za Javę i którzy chcą nadal sprzedawać aktualizacje. Dlatego zamierzają go przedłużyć, aby był „chłodny”. Tylko moje 2 centy.
Giorgio