Java 8: wrażenia

Jako wielbiciel języków funkcyjnych wyczekiwałem Javy 8 z pewną dozą niecierpliwości. To jedno móc zawsze napisać coś w Haskellu, a zupełnie co innego mieć takie rzeczy jak lambdy we współczesnym COBOLu jakim jest Java – mimo wszystko póki co mogę liczyć raczej na znalezienie pracy przy programowaniu w Javie, a nie w Haskellu. Tak więc wyczekiwałem, wyczekiwałem, zapomniałem i ostatecznie premiera Javy 8 wzięła mnie z zaskoczenia. Ale gdy to nastąpiło, nie marnowałem czasu i natychmiast zacząłem się bawić nowymi zabawkami (zaniedbując trochę bieżące zadania „ważne”). W pewnym momencie doszło nawet do tego, że wyczekiwałem końca weekendu, żeby wrócić do pracy…. Ale to już przeszło. Po paru tygodniach eksperymentowania, naginania możliwości i przekraczania granic tego, co możliwe, dochodzę do wniosku, że jest nieźle, ale mogło być lepiej.

Mniej lub bardziej rozczarowujących rzeczy pewnie mógłbym wymienić więcej, ale tylko jedna z nich rzuciła mi się w oczy z wystarczającą siłą, żeby o niej napisać coś więcej, mianowicie to, że interfejsy funkcyjne nie mogą rzucać wyjątków. Może to efekt działania frakcji wyjątko-sceptyków, którzy uważają, że wyjątki (te „checked”) są pomiotem Szatana i najchętniej by je wymietli z języka ogniem i mieczem (też przechodziłem przez tę fazę, ale doświadczenia „po drugiej stronie” uświadomiły mi jednak, że błędem było myśleć, że wyjątki są błędem). Może to celowe działanie wynikające z innych pobudek, takich jak chęć uniknięcia zbędnej komplikacji interfejsów i/lub implementacji, czy też chęć uniknięcia kłopotliwych sytuacji, do których często prowadzą wyjątki jeśli się nie wie co się robi (ale i tak muszą obsługiwać choćby RuntimeException więc co za różnica?). Może było to przedyskutowane na śmierć przez te wszystkie lata, kiedy dodanie lambd było odkładane do kolejnych wersji języka, i po prostu nie zainteresowałem się wystarczająco by mi się chciało poszukać argumentacji za i przeciw. W każdym razie doprowadza mnie do szewskiej pasji, że jeśli tylko pojawia się gdzieś np. operacja wejścia-wyjścia, mam do wyboru zaniechać takich bajerów jak strumienie (te z klasy Stream, nie strumienie bajtów i znakowe), albo pisać kod brzydki jak strzępiel. Mam całe biblioteki, które rzucają określone wyjątki, i bardzo słusznie, że to robią, z którymi miałbym co zrobić? Wykastrować obsługę błędów?…

Gdyby to ode mnie zależało, interfejs Function (i analogicznie cała reszta) zamiast tak:

interface Function<U, V> {
V apply(U u);
}

Wyglądałby tak:

interface Function<U, V, E extends Throwable> {
V apply(U u) throws E;
}

…I już mogę zrobić lambdę, która wywołuje Class.forName(). O! Wiem, że w ten sposób implementacja może rzucić tylko jeden typ wyjątku, ale lepszy rydz niż nic. (BTW: Nie wiedzieliście, że można robić takie cosie z generycznymi deklaracjami wyjątków, prawda?)

Ogólne wrażenie: dobre i to.

Reklamy

9 thoughts on “Java 8: wrażenia

  1. To w funkcyjnych stosuje się wyjątki?

    Myślałem, że jest strumień danych i monady do jego obsługi.
    Ostatnio dużo używam takiego strumieniowania i dość ciężko wsadzać tam wątki.
    Nawet w C#6 o tym wiedzą.

    W funkcyjnej części Pythona wyjątki są ukryte jako element interfejsu komunikacji. Np do sygnalizowania końca iteracji. Nie widać wyjątku, ale on tam jest.

    Polubienie

  2. Jak można znać Haskella i jeszcze mieć choć cień wątpliwości, że wyjątki (w wydaniu javowym czy pythonowym) są pomiotem Szatana?

    W języku funkcyjnym wszystko jest funkcją. Javowa (tudzież pythonowa) konstrukcja try/catch nie jest funkcją i nie można jej wygodnie odseparować od logiki. Ile już razy powstawał mi w Pythonie irytująco zagnieżdżony kod, albo ile razy powstrzymywałem się przed zajrzeniem do słownika wprost w obawie, że zaraz poleci KeyError i będzie trzeba dodać następne try/except? Nie zliczę.

    Drugi problem z javowymi wyjątkami jest taki, że kiedy czytam "int f(int x)…", to chciałbym wierzyć, że mam do czynienia z funkcją, która zaaplikowana do inta, zwróci mi inta. Nic bardziej błednego! Zamiast inta mogę wszak dostać wyjątek! To ma być statycznie typowany język? W pewnym sensie to nawet gorsze niż null, bo w funkcji int -> int nulla zwrócić się nie da, a wyjątek – jak najbardziej.

    I nie koniec na tym. Nie dość, że funkcja zwraca nie to, co powinna, to na dobitkę jeszcze potrzebuję orzydliwej konstrukcji składniowej, żeby tę nieprzepisową wartość złapać. I co jeszcze gorsze, nie mogę jej odseparować i zamknąć w sobnej funkcji, w module ukrytym pod zawiłym drzewie katalogów, żeby nie kłuł w oczy.

    Tak, wyjątki SĄ pomiotem Szatana. :)

    Polubienie

  3. Jezuch, ok, jeśli przyjąć taką optykę, to można uznać, że javowe wyjątki były krokiem naprzód. Ale czas przeszły jest w poprzednim zdaniu kluczowy. ;)

    Sprae, jeśli się ze mną zgadzasz, to porponuję zapoznanie się z Haskellem. Dzięki niesamowicie elastycznemu systemowi typów tego języka, możesz rzucać wyjątki nie naruszając deklaracji typu zwracanego przez funkcję. I to co najmniej na dwa różne sposoby. :)

    Dla kogoś, kto chwilę pobawi się z czysto funkcyjnym językiem, bardzo szybko staje się oczywiste, że funkcja jest to wyrażenie, którego wynik jest bezpośrednio podstawiany na miejsce wywołania. Ponieważ nie ma efektów ubocznych, zwracanie wyniku jest JEDYNYM sensem istnienia funkcji. Jeżeli funkcja może zbłądzić, to trzeba tak zaprojektować typ zwracany, aby był on w stanie tę nieprawidłowość odzwierciedlić. Bez żadnych magicznych składni typu try/catch. Jeśli chcemy być precyzyjni, Haskell też ma instrukcje throw oraz catch – tyle że jak wszystko w tym języky, są one funkcjami. Ze wszystkimi radosymi implikacjami tego faktu. :)

    Polubienie

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Wyloguj / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Wyloguj / Zmień )

Zdjęcie na Facebooku

Komentujesz korzystając z konta Facebook. Wyloguj / Zmień )

Zdjęcie na Google+

Komentujesz korzystając z konta Google+. Wyloguj / Zmień )

Connecting to %s