To jak to z tymi generykami jest?

2

Znów na forum powstala dyskusja o generykach w Javie i C#. Mógłby ktoś tak w skrócie nakreślić o co chodzi? Czemu niby te Javowe są zepsute? Co C# zrobil lepiej w tym temacie?
Może macie jakies linki gdzie jest to sensownie wyjaśnione?

Pozwole sobie przywołać specjalistów z obu stron:
@jarekr000000 @Wibowit @Afish @somekind

1

To jest chyba dość sensowne opracowanie różnic: http://www.jprl.com/Blog/archive/development/2007/Aug-31.html#jcs-implementation

2

Szablony z C++ są bardziej elastyczne niż generyki z Javy (co jest w C# to nie wiem), ale generują więcej kodu. Generyki w Javie to nakładka na rzutowanie bazowej klasy Object, prymitywna rzecz ale w większości przypadków działa.
BTW najlepsze typy generyczne są oczywiście w Haskellu :p

2

Podstawowa różnica - na korzyść C#
https://schneide.blog/2015/05/11/declaration-site-and-use-site-variance-explained/

Declaration site variance - jeszcze ze 2 lata temu nie rozumiałem czemu generyki w Scali i Kotlinie pisze się "jakoś łatwiej" (declaration site variance jak C#). Do dziś nie potrafię wytłumaczyć dokładnie czemu, bo w teorii oba podejścia mają zady i walety, ale jednak declaration site jest po prostu "naturalniejsze" i jakoś mniejsze koszmarki wychodzą.

1
KamilAdam napisał(a):

Szablony z C++ są bardziej elastyczne niż generyki z Javy (co jest w C# to nie wiem), ale generują więcej kodu. Generyki w Javie to nakładka na rzutowanie bazowej klasy Object, prymitywna rzecz ale w większości przypadków działa.

BTW najlepsze typy generyczne są oczywiście w Haskellu :p

Z tego co pamiętam to te szablony w C++ się zrypane, to znaczy nie możesz zrobić Vector<T>: Collection<T> (a w Javie ArrayList<T> może implementować Collection<T>).
tj klasa generyczna nie może dziedziczyć po klasie generycznej, może co najwyżej po konkretnym uszczegółowieniu.
A o błędach kompilatora cep plus plus z szablonami to nawet nie będę mówił :/

Edit: dobra, być moze się mylę, niech mnie ktoś wyjaśni.

3
kzkzg napisał(a):

Znów na forum powstala dyskusja o generykach w Javie i C#. Mógłby ktoś tak w skrócie nakreślić o co chodzi? Czemu niby te Javowe są zepsute? Co C# zrobil lepiej w tym temacie?
Może macie jakies linki gdzie jest to sensownie wyjaśnione?

Najpierw trzeba zdefiniować co to znaczy zepsute. Statyczne typowanie w Javie i w C# jest sound (pod warunkiem, że nie ignoruje się ostrzeżeń kompilatora o zgodności typów), co najwyżej pomijając tablice. Klasyczny przykład problemów z tablicami (C# odziedziczył je po Javie, chociaż miał okazję to naprawić, skoro był robiony od zera):

// w Javie pada
String[] strings = new String[1];
Object[] objects = strings;
objects[0] = 5; // exception
// w C# też pada
string[] strings = new string[1];
object[] objects = strings;
objects[0] = 5; // exception

To czego w Javie nie da się zrobić to dynamicznego sprawdzania typów generycznych czy np. przeciążania metod bazując na parametrach generycznych. Za przeciążaniem w ogólności nie przepadam. Dynamicznego sprawdzania typów generycznych generalnie nie robię, bo (primo) JVM tego nie wspiera (z powodu wymazywania typów generycznych), chociaż Scala (w której na co dzień programuję) ma pewne objeścia (https://docs.scala-lang.org/overviews/reflection/typetags-manifests.html), a także (secundo) dlatego, że zwykle wygląda to jak droga na skróty zamiast zrobić porządną hierarchię typów (ale zaznaczę jeszcze raz, że programuję w Scali, a ta ma dużo bardziej rozbudowany system typów niż Java czy C#). Dynamicznego sprawdzania typów generycznych brakowało mi tylko jak optymalizowałem programy pod mikrobenchmarki, ale i tak bez chociażby https://openjdk.java.net/projects/valhalla/ mikrooptymalizacje w Javie są karkołomne i niekoniecznie dają spodziewany efekt.

W typowych biznesowych krowach, czyli w 99% codebase w Javie i C#, różnice między implementacjami generyków nie mają większego znaczenia. Jeśli ktoś twierdzi, że jest inaczej to może niech poda jakieś kontrprzykłady.

1
Wibowit napisał(a):

W typowych biznesowych krowach, czyli w 99% codebase w Javie i C#, różnice między implementacjami generyków nie mają większego znaczenia. Jeśli ktoś twierdzi, że jest inaczej to może niech poda jakieś kontrprzykłady.

Generyki dla typów wartościowych, optymalizacje na tychże, sensowne typy dla lambd (jedno Action i jedno Func zamiast syfu Consumer, Predicate, BiFunction i pieron wie co jeszcze, a na końcu i tak trzeba własny interfejs robić), refleksja na typach generycznych i brak konieczności przekazywania jakiegoś class na każdym kroku.

2
Afish napisał(a):
Wibowit napisał(a):

W typowych biznesowych krowach, czyli w 99% codebase w Javie i C#, różnice między implementacjami generyków nie mają większego znaczenia. Jeśli ktoś twierdzi, że jest inaczej to może niech poda jakieś kontrprzykłady.

Generyki dla typów wartościowych, optymalizacje na tychże, sensowne typy dla lambd (jedno Action i jedno Func zamiast syfu Consumer, Predicate, BiFunction i pieron wie co jeszcze, a na końcu i tak trzeba własny interfejs robić)

To dotyczy optymalizacji właśnie. Generyków dla typów wartościowych na razie nie ma, co sprawia, że generyki są (zwykle tylko nieco) mniej wydajne, ale pisanie List<Integer> zamiast List<int> nie jest wielką filozofią. Jak w końcu wejdzie ten https://openjdk.java.net/projects/valhalla/ to będzie można pisać List<int>. Czy to będzie jakaś wielka rewolucja? Moim zdaniem nie. Niektórzy robią z tego przesadzoną sensację, a w kodzie biznesowym znaczenie tego jest niewielkie.

BiFunction, Predicate, etc to też optymalizacje po to by uniknąć boxingu. Są już gotowe i się tego używa. Jak często się pisze własne to nie wiem, bo dawno nie pisałem w Javie, ale obstawiam, że bardzo rzadko (ktoś piszący dużo w Javie może mnie tutaj poprawić).

refleksja na typach generycznych i brak konieczności przekazywania jakiegoś class na każdym kroku.

Widziałem sporo kodu w Javie, ale za to mało przekazywania jakiegoś class na każdym kroku. Jak chcesz przenieść 1:1 jakiś istniejący kod C# do Javy to pewnie będzie dużo takich kombinacji, ale równie dobrze można powiedzieć, że po przeszczepieniu 1:1 kodu z Pythona do C# wynikowy kod będzie wyglądał dużo brzydziej niż oryginalny.

1
Wibowit napisał(a):

To dotyczy optymalizacji właśnie. Generyków dla typów wartościowych na razie nie ma, co sprawia, że generyki są (zwykle tylko nieco) mniej wydajne, ale pisanie List<Integer> zamiast List<int> nie jest wielką filozofią. Jak w końcu wejdzie ten https://openjdk.java.net/projects/valhalla/ to będzie można pisać List<int>. Czy to będzie jakaś wielka rewolucja? Moim zdaniem nie. Niektórzy robią z tego przesadzoną sensację, a w kodzie biznesowym znaczenie tego jest niewielkie.

Wydajnościowo jest to bardzo duża zmiana, pewnie jak Java to wprowadzi, to będzie zachwyt przez kilka lat.

BiFunction, Predicate, etc to też optymalizacje po to by uniknąć boxingu. Są już gotowe i się tego używa. Jak często się pisze własne to nie wiem, bo dawno nie pisałem w Javie, ale obstawiam, że bardzo rzadko (ktoś piszący dużo w Javie może mnie tutaj poprawić).

No cóż, piszę w Javie i moim zdaniem jest to ból. Tak średnio co kilka godzin nadziewam się na to, że nie pamiętam nazwy (bo jak to tak zrobić jedną nazwę, tak się przecież nie da), a co kilka dni na brak sensownego typu i muszę jakieś Guavy dociągać dla TriFunction czy coś.

Widziałem sporo kodu w Javie, ale za to mało przekazywania jakiegoś class na każdym kroku. Jak chcesz przenieść 1:1 jakiś istniejący kod C# do Javy to pewnie będzie dużo takich kombinacji, ale równie dobrze można powiedzieć, że po przeszczepieniu 1:1 kodu z Pythona do C# wynikowy kod będzie wyglądał dużo brzydziej niż oryginalny.

No cóż, wiele razy to widziałem, nie da się pisać generycznego kodu, jak trzeba przekazywać class, więc nic dziwnego, że Javowcy tego nie robią.

2
Afish napisał(a):
Wibowit napisał(a):

To dotyczy optymalizacji właśnie. Generyków dla typów wartościowych na razie nie ma, co sprawia, że generyki są (zwykle tylko nieco) mniej wydajne, ale pisanie List<Integer> zamiast List<int> nie jest wielką filozofią. Jak w końcu wejdzie ten https://openjdk.java.net/projects/valhalla/ to będzie można pisać List<int>. Czy to będzie jakaś wielka rewolucja? Moim zdaniem nie. Niektórzy robią z tego przesadzoną sensację, a w kodzie biznesowym znaczenie tego jest niewielkie.

Wydajnościowo jest to bardzo duża zmiana, pewnie jak Java to wprowadzi, to będzie zachwyt przez kilka lat.

Bardzo duża zmiana będzie w mikrobenchmarkach. Dużo ciekawsza będzie opinia przeciętnego Javowca. Czy nagle wszystkim Java wystrzeli jak rakieta? Aplikacje biznesowe naklepane w Javie będą działać 2x szybciej?

BiFunction, Predicate, etc to też optymalizacje po to by uniknąć boxingu. Są już gotowe i się tego używa. Jak często się pisze własne to nie wiem, bo dawno nie pisałem w Javie, ale obstawiam, że bardzo rzadko (ktoś piszący dużo w Javie może mnie tutaj poprawić).

No cóż, piszę w Javie i moim zdaniem jest to ból. Tak średnio co kilka godzin nadziewam się na to, że nie pamiętam nazwy (bo jak to tak zrobić jedną nazwę, tak się przecież nie da), a co kilka dni na brak sensownego typu i muszę jakieś Guavy dociągać dla TriFunction czy coś.

W vavr.io są sensownie ponazywane funkcje, od https://www.javadoc.io/doc/io.vavr/vavr/0.10.4/io/vavr/Function0.html do https://www.javadoc.io/doc/io.vavr/vavr/0.10.4/io/vavr/Function8.html

Widziałem sporo kodu w Javie, ale za to mało przekazywania jakiegoś class na każdym kroku. Jak chcesz przenieść 1:1 jakiś istniejący kod C# do Javy to pewnie będzie dużo takich kombinacji, ale równie dobrze można powiedzieć, że po przeszczepieniu 1:1 kodu z Pythona do C# wynikowy kod będzie wyglądał dużo brzydziej niż oryginalny.

No cóż, wiele razy to widziałem, nie da się pisać generycznego kodu, jak trzeba przekazywać class, więc nic dziwnego, że Javowcy tego nie robią.

Ile takiego kodu jest? Zarzut o brak reified generics to w sumie można by zastosować do każdego języka, który nie jest C#-em i (hmm) chyba z tego wynika, że "tylko w C# da się pisać generyczny kod".

1
Wibowit napisał(a):

Bardzo duża zmiana będzie w mikrobenchmarkach. Dużo ciekawsza będzie opinia przeciętnego Javowca. Czy nagle wszystkim Java wystrzeli jak rakieta? Aplikacje biznesowe naklepane w Javie będą działać 2x szybciej?

Nie wiem, nie wnikam. Jak Java wprowadziła streamy, to kilka lat trzeba było rysować diagramy, jak to działa i różni się od pętli, najlepszy wynalazek zaraz po chlebie krojonym. Zresztą, Ty sam dość często piszesz o tych wszystkich cudach JVM, a przecież one jeszcze nie wyszły, to co dopiero będzie, jak one już wejdą na proda.

W vavr.io są sensownie ponazywane funkcje, od https://www.javadoc.io/doc/io.vavr/vavr/0.10.4/io/vavr/Function0.html do https://www.javadoc.io/doc/io.vavr/vavr/0.10.4/io/vavr/Function8.html

No, w vavr, a nie w bibliotece standardowej.

Ile takiego kodu jest? Zarzut o brak reified generics to w sumie można by zastosować do każdego języka, który nie jest C#-em i (hmm) chyba z tego wynika, że "tylko w C# da się pisać generyczny kod".

Nie, nie tylko w C#. No oczywiście, że w Javie takiego kodu jest mało, bo się go topornie pisze. W innych językach takich rzeczy jest dużo, bo język to sensownie wspiera. C#, C++, Haskell i tak dalej.

7
Wibowit napisał(a):

To dotyczy optymalizacji właśnie. Generyków dla typów wartościowych na razie nie ma, co sprawia, że generyki są (zwykle tylko nieco) mniej wydajne, ale pisanie List<Integer> zamiast List<int> nie jest wielką filozofią. Jak w końcu wejdzie ten https://openjdk.java.net/projects/valhalla/

Valhalla wejdzie dokładnie w tym samym roku, który będzie rokiem linuxa na desktopie.

0
Afish napisał(a):
Wibowit napisał(a):

Bardzo duża zmiana będzie w mikrobenchmarkach. Dużo ciekawsza będzie opinia przeciętnego Javowca. Czy nagle wszystkim Java wystrzeli jak rakieta? Aplikacje biznesowe naklepane w Javie będą działać 2x szybciej?

Nie wiem, nie wnikam. Jak Java wprowadziła streamy, to kilka lat trzeba było rysować diagramy, jak to działa i różni się od pętli, najlepszy wynalazek zaraz po chlebie krojonym. Zresztą, Ty sam dość często piszesz o tych wszystkich cudach JVM, a przecież one jeszcze nie wyszły, to co dopiero będzie, jak one już wejdą na proda.

Wprowadzenie LINQ do C# też spowodowało wielkie halo. Zarówno streamy z Javy 8+ jak i LINQ w C# zmieniły sporo w wyglądzie kodu. Natomiast okazjonalna zmiana z List<Integer> na List<int> wygląda przy tym na zmianę kosmetyczną.

W vavr.io są sensownie ponazywane funkcje, od https://www.javadoc.io/doc/io.vavr/vavr/0.10.4/io/vavr/Function0.html do https://www.javadoc.io/doc/io.vavr/vavr/0.10.4/io/vavr/Function8.html

No, w vavr, a nie w bibliotece standardowej.

Generalnie zgadzam się, że te typy dla funkcji w bibliotece standardowej Javy pozostawiają sporo do życzenia, ale nadal nie jestem przekonany co do skali wpływu tego na typowy biznesowy kod.

Ile takiego kodu jest? Zarzut o brak reified generics to w sumie można by zastosować do każdego języka, który nie jest C#-em i (hmm) chyba z tego wynika, że "tylko w C# da się pisać generyczny kod".

Nie, nie tylko w C#. No oczywiście, że w Javie takiego kodu jest mało, bo się go topornie pisze. W innych językach takich rzeczy jest dużo, bo język to sensownie wspiera. C#, C++, Haskell i tak dalej.

Zwykle jak szukam informacji o typach w Haskellu to znajduję informację, że typy są tam wymazywane, np:
https://softwareengineering.stackexchange.com/questions/220728/are-types-erased-in-haskell

There needs to be made a distinction between "parametric" and "ad-hoc overloading" polymorphism. Parametric means "behaves uniformly for all types a", whereas "ad-hoc" -- what Simon refers to as polymorphic -- changes implementation based on the type.

Examples of both are reverse :: [a] -> [a], which is parametric, and show :: Show a => a -> String which is "ad-hoc" overloaded.

(...)

Both are resolved at compile-time, however, and in fact "erased". Modulo various GHC extensions and Template Haskell, etc. Types are in fact erased at compile time and never inspected on run-time.

3
Wibowit napisał(a):

Wprowadzenie LINQ do C# też spowodowało wielkie halo. Zarówno streamy z Javy 8+ jak i LINQ w C# zmieniły sporo w wyglądzie kodu. Natomiast okazjonalna zmiana z List<Integer> na List<int> wygląda przy tym na zmianę kosmetyczną.

No, to potwierdza, że jak wejdą Valhalle i inne cuda, to będzie o tym bardzo, bardzo głośno w świecie Javy. Już nie mogę się doczekać tych setek artykułów tłumaczących czym są te "inline class" (czy jak to się teraz zwie), ile będzie pokazywania przekazywania parametrów do funkcji i tak dalej.

Generalnie zgadzam się, że te typy dla funkcji w bibliotece standardowej Javy pozostawiają sporo do życzenia, ale nadal nie jestem przekonany co do skali wpływu tego na typowy biznesowy kod.

Spoko, możemy się odbijać argumentami na bycie przekonanym lub nie, to nie jest meritum.

Zwykle jak szukam informacji o typach w Haskellu to znajduję informację, że typy są tam wymazywane, np:

Ale to nie chodzi o to, że typy są wymazywane, tylko o to, że nie da się pisać sensownie generycznego kodu. W dotnecie reifikowane generyki też są problematyczne, bo CLR nie wspiera HKT, więc jest to plus dla JVM. W Scali da się o wiele sensowniej pisać kod generyczny, mimo że działa na tej samej platformie. Erasure sam w sobie nie jest zły ani dobry.

0
Afish napisał(a):
Wibowit napisał(a):

Wprowadzenie LINQ do C# też spowodowało wielkie halo. Zarówno streamy z Javy 8+ jak i LINQ w C# zmieniły sporo w wyglądzie kodu. Natomiast okazjonalna zmiana z List<Integer> na List<int> wygląda przy tym na zmianę kosmetyczną.

No, to potwierdza, że jak wejdą Valhalle i inne cuda, to będzie o tym bardzo, bardzo głośno w świecie Javy. Już nie mogę się doczekać tych setek artykułów tłumaczących czym są te "inline class" (czy jak to się teraz zwie), ile będzie pokazywania przekazywania parametrów do funkcji i tak dalej.

O Javie to generalnie jest mnóstwo artykułów i tak już zostanie. Dla przykładu, artykułów o tym, że "Java is dying" było co najmniej miliard. Nawet gospodynie domowe musiały o tym czytać, bo tak się temat nosił.

Generalnie zgadzam się, że te typy dla funkcji w bibliotece standardowej Javy pozostawiają sporo do życzenia, ale nadal nie jestem przekonany co do skali wpływu tego na typowy biznesowy kod.

Spoko, możemy się odbijać argumentami na bycie przekonanym lub nie, to nie jest meritum.

To może napiszę o swoich doświadczeniach w Scali, języku dużo bardziej polegającym na lambdach niż Java. Otóż piszę kod biznesowy w stylu mieszanym FP + imperatywny OOP (czyli bez monad IO i szaleństw a'la Hascalator/Haskellator) i generalnie rzadko zdarza mi się pisać deklarację typu funkcji. W 95%+ lambdy wykorzystywane są tak, że je definiuję w miejscu, w którym są oczekiwane, bez podawania jawnych typów. Tutaj składnia się mocno nie różni, w Javie, C#, Scali, JSie, itd wygląda mniej więcej tak:

(param1, param2, param3) => { ciało lambdy }

Może jestem dziwny (obstawiam, że nie), ale rzędy wielkości częściej definiuję lambdę (do czego jest zwięzły cukier składniowy w Javie) niż deklaruję jej typ (do czego nie ma jeszcze cukru w Javie).

Zwykle jak szukam informacji o typach w Haskellu to znajduję informację, że typy są tam wymazywane, np:

Ale to nie chodzi o to, że typy są wymazywane, tylko o to, że nie da się pisać sensownie generycznego kodu. W dotnecie reifikowane generyki też są problematyczne, bo CLR nie wspiera HKT, więc jest to plus dla JVM. W Scali da się o wiele sensowniej pisać kod generyczny, mimo że działa na tej samej platformie. Erasure sam w sobie nie jest zły ani dobry.

Wspomniane HKT, a także inne zaawansowane zabawy z typami, to nie jest zabawa dla typowego klepacza kodu. Typowe zastosowanie Javy to pisanie względnie prostego kodu, który ma implementować nudne wymogi biznesowe, np. parsujemy X typów badziewnych raportów czy innych dokumentów, tworzymy Y obleśnych formularzy, integrujemy się z Z nieprzyjemnych w obsłudze API zewnętrznych. W takich przypadkach, przy założeniu że trzymamy się imperatywnego OOPa, to moim zdaniem aktualna Java jest adekwatna (w sensie jej generyki). W żadnym miejscu nie polecałem języka Java (nie mylić z platformą Java) do zaawansowanych zabaw z typami czy do programowania funkcyjnego w ogólności.

@jarekr000000 wspomniał w komentarzu o https://openjdk.java.net/projects/loom/ (projekt dotyczący między innymi modelu wielowątkowości). To jest coś co moim zdaniem będzie miało rzędy wielkości większy wpływ na postać kodu biznesowego niż https://openjdk.java.net/projects/valhalla/ (bo typowy klepacz kodu biznesowego i tak nawet nie widzi, że pętla jest O(n*n), a mogłaby być szybko przerobiona na O(n), więc co tu mówić o innych optymalizacjach). W ostatniej dekadzie jest bardzo dużo ruchu w temacie programowania nieblokującego czy też asynchronicznego (te pojęcia są mylone, w sumie to ciężko rozróżnić co kto ma na myśli czasem) i powstało dużo różnych rozwiązań, z których większość jest nieprzyjazna programiście, cierpi na problem kolorowania funkcji i zaśmieca kod biznesowy. Dla przykładu:

  • callback hell (ale to już generalnie przeszłosć i raczej jest zawężone do starego kodu JSowego)
  • operowanie na Task<T> (czy też nazwane Future, CompletableFuture, ReactiveBullcrap, cokolwiek) wymaga ciągłego używania kombinatorów a'la andThen, thenMap itp itd Problem jest odczuwalny nawet w Scali, mimo niemałych ilości cukru składniowego.
  • async/await jest bardziej przyjazną składnią, ale dalej cierpi na problem kolorowania funkcji. Node.js ma podobno dużo zduplikowanych funkcji z których jeden zestaw jest synchroniczny, a drugi nie.

Natomiast Project Loom wprowadza lekkie wirtualne wątki (tak teraz je nazywają, virtual threads, a wcześniej było fibers), które można i nawet należy blokować, bo są bardzo lekkie, a przełączanie między nimi jest bardzo tanie (JVMka może spokojnie skalować się do milionów wirtualnych wątków jednocześnie). Bardzo możliwe, że (w Javie po wprowadzeniu project loom) concurrency będzie zrobione nawet lepiej niż w golangu, a przecież głównym atutem Go jest właśnie programowanie współbieżne.

Podsumowując:
W moich rozważaniach skupiam się na wykorzystywaniu Javy do typowych zastosowań biznesowych, a nie jako języku do wszystkiego. W typowych zastosowaniach biznesowych język Java radzi sobie całkiem dobrze (chociaż jest dość brzydki), ale nawet nie aspiruje do bycia językiem spełniającym wszystkie oczekiwania.

1
Wibowit napisał(a):

To może napiszę o swoich doświadczeniach w Scali, języku dużo bardziej polegającym na lambdach niż Java. Otóż piszę kod biznesowy w stylu mieszanym FP + imperatywny OOP (czyli bez monad IO i szaleństw a'la Hascalator/Haskellator) i generalnie rzadko zdarza mi się pisać deklarację typu funkcji. W 95%+ lambdy wykorzystywane są tak, że je definiuję w miejscu, w którym są oczekiwane, bez

Ciekawe, że przez dwa lata Kotlinowania przeszedłem dość szybko od pisania kodu ala OOP bez mutowania do prawie czystego FP, z monadami, typami funkcyjnymi i na końcu nawet klasy poszły się troche rypać (nie całkiem) - zostały funkcje, rekordy (data class) i unie (sealed class). Do tego rypiąc dość nudne enterprisy (ale nie typowe crudy - miałem dużo obróbki strumieni PDF, albo strumieni XML :-) ).

klasy poszły się troche rypać - tu wyjaśnienie, bo ja często robie skróty - w kotlinie można sobie pisać, jak w starych językach funkcje top level, nie trzeba umieszczać w żadnej klasie,
Więc troche klas (np. utilsów) mi to wyeliminowało, zostały przeważnie klasy związane z DI (ręcznym), bo jednak pola się w kontekście czasem przydają (coby się w monady (Reader) nie bawić).
Wyleciały typowe klasy ala java z domeny, choć jak ktoś się czepi to może powiedzieć, że nic się nie zmieniło - kwestia czy patrzymy od bajtkodu czy od tego jak się tym posługuje (i bieda/kotlin pattern matching vs polimorfizm).

Pisałem z ludźmi o różnym poziomie zrypania fp - raczej nie mocno. Ale byłem mocno pozytywnie zaskoczony, jedn kumpel, który miał FP zupełnie w d..pie jechał ładny kod funkcyjny jakby nic innego nie robił...

Żeby nie był off top - używam reified generics :-), które w kotlinie są baaardzo biedne, ale miejscami są.

0

To jak się już chwalimy użyciem FP w nudnym kodzie biznesowym to ja odkryłem że znalazłem zastosowanie dla Monady Writer. Oczywiście wyszło mi jak zawsze od D... strony. Czyli najpierw nabazgroliłem klasę case class WithNotes[E, A](notes: Seq[E], value: A) w metodami map i flatMap. W tydzień na to patrzyłem zanim się zorientowałem że to Monada Writer dla ubogich. Monada Writer w normalnym kodzie biznesowym walidujacym arkusz kalkulacyjny. No przecież takie rzeczy się nie zdarzają :D

1
Wibowit napisał(a):

rzadko zdarza mi się pisać deklarację typu funkcji.

No w scali masz sensowne typy func, więc nic dziwnego.

Wspomniane HKT, a także inne zaawansowane zabawy z typami, to nie jest zabawa dla typowego klepacza kodu.

No w Javie do HKT jest bardzo długa droga, bo tam nawet pisanie zwykłych typów generycznych jest utrudnione (jak już wspomniałem).

Reszty posta nie komentuję, nie chce mi się offtopować na temat looma kolejny raz.

0
Afish napisał(a):
Wibowit napisał(a):

rzadko zdarza mi się pisać deklarację typu funkcji.

No w scali masz sensowne typy func, więc nic dziwnego.

Nie o to chodziło jakie są typy funkcji, tylko o to, że rzadko trzeba je wklepywać w kod. Inaczej mówiąc: mimo, że w Scali jest cukier zarówno na deklarację typu jak i na definicję funkcji to i tak rzadko kiedy pojawia się (u nas) np: def hof(func: (A, B) => C), a dużo częściej pojawia się np: hof((a, b) => { ... }). Dużo większe znaczenie niż cukier na typ funkcji (którego jeszcze nie ma w Javie) ma cukier na reprezentację instancji lambdy (który już jest w Javie 8+).

Wspomniane HKT, a także inne zaawansowane zabawy z typami, to nie jest zabawa dla typowego klepacza kodu.

No w Javie do HKT jest bardzo długa droga, bo tam nawet pisanie zwykłych typów generycznych jest utrudnione (jak już wspomniałem).

Jeśli 1000x powtórzysz, że "w Javie nie da się programować generycznie" i podasz 0 przykładów (z typowego kodu biznesowego) to dalej niewiele udowodnisz.

1
Wibowit napisał(a):

Jeśli 1000x powtórzysz, że "w Javie nie da się programować generycznie" i podasz 0 przykładów (z typowego kodu biznesowego) to dalej niewiele udowodnisz.

No ale ja Ci już podałem przykłady. Za każdym razem, gdy w Javie muszę przekazywać jakieś głupie .class, bo kompilator wymazuje typy i nie potrafi ich sensownie obsłużyć, za każdym razem gdy próbuję zrobić instanceof jakiegoś generyka i kompilator przypomina mi, że to coś się wymaże, za każdym razem, gdy chcę napisać funkcję dla List<int> i przypominam sobie, że tutaj jest List<Integer>, za każdym razem gdy robię interfejs dla jakiegoś TriFunction (i jeszcze do tego muszę certolić się z checked exceptions), to przypominam sobie, że w innych językach jest lepiej. Jasne, możesz napisać, że 99% kodu biznesowego nie wymaga takich rzeczy, tylko tutaj popełniasz błąd logiczny - jak nie dasz ludziom sensownych generyków, to nic dziwnego, że nie piszą kodu używającego sensownych generyków.

Podałem Ci przykłady, gdzie generyki dotnetowe są sensowniejsze i ma to wymierne korzyści, Ty twierdzisz, że to nie jest potrzebne i masz rację - da się pisać kod biznesowy bez tego. Tak jak dało się pisać kod biznesowy bez lambd i streamów.

0

TL;DR
Cały pakiet java.util.function pokazuje co jest nie tak z generykami w Javie.

Jeśli chodzi o Valhalle to chyba szybciej przejdę na Haskella czy jakiegoś kwantowego Lispa niż ona wyjdzie.

0

TL;DR
Cały pakiet java.util.function pokazuje co jest nie tak z generykami w Javie.

Możesz bardziej rozjaśnić co tam jest nie tak?

0
Wibowit napisał(a):

Wprowadzenie LINQ do C# też spowodowało wielkie halo.

To chyba u Javowców było to wielkie halo, bo przez następną dekadę mogli ględzić, że to tylko bezsensowny i zbędny cukier składniowy. Aż w końcu łaska przewspaniałej Wyroczni na nich spadła i okazało się, że nie trzeba pisać pętli i ifów aby przefiltrować kolekcję, i nagle typowy kod może być o 1/3 krótszy. Oczywiście potem zaczęło się gadanie jaki Java to zaawansowany język, i jak wyprzedza konkurencję. :D

2

Bardzo duża zmiana będzie w mikrobenchmarkach. Dużo ciekawsza będzie opinia przeciętnego Javowca. Czy nagle wszystkim Java wystrzeli jak rakieta? Aplikacje biznesowe naklepane w Javie będą działać 2x szybciej?

Tu się zgadzam z Wibowitem. Javowcy piszący CRUDy nie zauważą zmiany tak jak nie zauważyli wpływu EscapeAnalysis, ani tak jak nie przeszkadza im brak AVX i możliwości robienia wywołań systemowych. Valhalli raczej też nie zauważą.

Natomiast ludzie piszący kod wysokiej wydajności zdają sobie sprawę z tego, że Valhalla to jest kropla w morzu potrzeb i w zasadzie dla nich niewiele zmieni.

Problemem jest to, że:

  • kompilatory dostępne w JVMach są generalnie bardzo słabe w optymalizacji kodu, znacznie słabsze od tego co potrafi robić GCC/LLVM z kodem w C, C++, Rust, więc mało kto wybiera Javę do pisania szybkiego kodu
  • środowisko Javowe w przeważającej większości nie wykształciło kultury pisania wydajnego kodu (w przeciwieństwie do środowisk programistów C, C++, Rusta itp) - JVM może nawet po 10 latach dostać nawet najlepsze narzędzia do zrobienia czegoś wydajnie, a ludzie i tak zapaskudzą kod wszędzie klasami z tysiącem poziomów zagnieżdżeń, referencji, wywołań wirtualnych i wydajnościowo będzie to kupa.

Ten drugi problem w sumie jest poważniejszy - powoduje on to, że np. problemy wydajnościowe zgłaszane w projektach otwartoźródłowych, tudzież w JVMie nie są naprawiane latami.

2

@Krolik: bo programiści są drożsi od sprzętu, łatwiej dokupić więcej serwerów niż wyciskać lepszą wydajność. Dodatkowo liczy się czytelność i prostota kodu a ta często jest w konflikcie z wydajnością.

2

środowisko Javowe w przeważającej większości nie wykształciło kultury pisania wydajnego kodu

bo w tym przypadku to by robilo wiecej szkody niz pozytku

0
Krolik napisał(a):

Tu się zgadzam z Wibowitem. Javowcy piszący CRUDy nie zauważą zmiany tak jak nie zauważyli wpływu EscapeAnalysis, ani tak jak nie przeszkadza im brak AVX i możliwości robienia wywołań systemowych. Valhalli raczej też nie zauważą.

Przykłady z innych platform pokazują, że nie macie racji, frameworki bardzo przyspieszą (rzędu kilkudziesięciu procent) (mówię o wielu inicjatywach, nie o samej Valhalli).

7
Krolik napisał(a):
  • środowisko Javowe w przeważającej większości nie wykształciło kultury pisania wydajnego kodu (w przeciwieństwie do środowisk programistów C, C++, Rusta itp) - JVM może nawet po 10 latach dostać nawet najlepsze narzędzia do zrobienia czegoś wydajnie, a ludzie i tak zapaskudzą kod wszędzie klasami z tysiącem poziomów zagnieżdżeń, referencji, wywołań wirtualnych i wydajnościowo będzie to kupa.

Ale czy to naprawdę jest problem? Targetem Javy prawie od zawsze były aplikacje biznesowej niekrytycznej wydajności. Wszyscy wiedzą że jeśli gdzieś jest krytyczna wydajność to używa się C++ a od niedawna Rusta. Celem użycia Javy jest posiadanie aplikacji biznesowej którą można utrzymywać latami przy użyciu studentów, a nie mistrzu wiedzących czy ma metoda jest wirtualna czy nie wirtualna

4
Krolik napisał(a):

Problemem jest to, że:

  • kompilatory dostępne w JVMach są generalnie bardzo słabe w optymalizacji kodu, znacznie słabsze od tego co potrafi robić GCC/LLVM z kodem w C, C++, Rust, więc mało kto wybiera Javę do pisania szybkiego kodu
  • środowisko Javowe w przeważającej większości nie wykształciło kultury pisania wydajnego kodu (w przeciwieństwie do środowisk programistów C, C++, Rusta itp) - JVM może nawet po 10 latach dostać nawet najlepsze narzędzia do zrobienia czegoś wydajnie, a ludzie i tak zapaskudzą kod wszędzie klasami z tysiącem poziomów zagnieżdżeń, referencji, wywołań wirtualnych i wydajnościowo będzie to kupa.

Ten drugi problem w sumie jest poważniejszy - powoduje on to, że np. problemy wydajnościowe zgłaszane w projektach otwartoźródłowych, tudzież w JVMie nie są naprawiane latami.

Aplikacje biznesowe już ze 20 lat temu zostały przepisane z C++ do Javy i C# i nie ma powrotu do C++ czy Rusta. Dzisiejszy C++ jest niby dużo lepszy niż C++ sprzed 20 lat, ale i tak nikt go nie wybiera do pisania nowych aplikacji biznesowych. Zarówno Java jak i C# to klasy z tysiącem poziomów zagnieżdżeń, referencji, wywołań wirtualnych, a do tego w tle jest garbage collector, JIT i inne takie.

Nie rozumiem po co te ciągłe wstawki o C++ i Ruście robisz. Jak będzie dyskusja PHP vs Python to też napiszesz, że Rust szybszy?

Jeśli chodzi o wydajność w projektach biznesowych to największy wpływ mają:

  • złożoność obliczeniowa i pamięciowa - np. O(n^2) vs O(n lg n)
  • zewnętrzne komponenty, czyli np: zapytania do bazy danych
2

Wydajność to na ogół słaby argument, a przynajmniej tak długo jak używany jest przeciwko tobie, bo w odwrotnej sytuacji jest bardzo dobry screenshot-20211129212512.png

A przynajmniej taką tendencje dostrzegłem w waszych konsyderacjach ;)

1 użytkowników online, w tym zalogowanych: 0, gości: 1