Czemu Pan powiedział, że immutability jest dobre ?

Czemu Pan powiedział, że immutability jest dobre ?
TI
  • Rejestracja:prawie 7 lat
  • Ostatnio:3 miesiące
  • Postów:11
0

Siemka,
na forum często przewijają się rady, by stosować niemutowalne obiekty, nie łapię czemu to jest dobre, stąd moje pytanie, gdzie i dlaczego stosujecie tego typu obiekty i jakie przesłanki za tym stoją? Druga kwestia to, kiedy stosujecie tylko mutowalne obiekty.

Najczęstsze dwa powody ZA o których czytałem przywołam krótko poniżej.
Niemutowalność daje nam to, że stan obiektu na który wskazuje się nie zmieni, czyli nie muszę się martwić, że ktoś gdzieś bez mojej wiedzy nie zmieni mi stanu obiektu np. w funkcji którą wywołuję. Mam wrażenie, że to paranoidalne zabezpieczenie można porównać do odcięcia sobie dłoni, bo boimy się, że oparzymy sobie palec.

Kolejny argument to wielowątkowość, to jasne że immutability będzie thread-safe, bo nie musimy bawić się w synchronizację pomiędzy wątkami. To będzie dla mnie użyteczny tylko wtedy, gdy chcemy obiekt przekazać do drugiego wątku i nie obchodzi nas, co ten wątek tam z nim zrobi.

Abstrahując od samego designu to każda zmiana pola tworzy nowe obiekty, co wywołuję częściej GC, bo jest zmuszony ciągle czyścić pamięć ze zmienionych obiektów.

lion137
  • Rejestracja:około 8 lat
  • Ostatnio:mniej niż minuta
  • Postów:4927
0

Sam sobie częściowo Odpowiedziałeś na pytanie:) Jak Chcesz więcej Zajrzyj tutaj:
https://web.mit.edu/6.005/www/fa16/classes/09-immutability/
lub ten wątek: https://stackoverflow.com/questions/5652652/java-advantages-of-of-immutable-objects-in-examples
Ogólnie, to STFW:), bo jest na ten temat sporo.


edytowany 1x, ostatnio: lion137
TI
@lion137: dzieki, nadrobię lekture, czytałem kilka innych wątków na stacku, ale było tam klepanie mantry bez dohlebnej analizy dlaczego
Althorion
Moderator C/C++
  • Rejestracja:prawie 10 lat
  • Ostatnio:około 20 godzin
  • Postów:1605
1

To pierwsze to nie jest paranoja, tylko zdrowy rozsądek przy jakimkolwiek większym projekcie, przy którym pracuje co najmniej dwóch ludzi. Czasem aż się chce „ułatwić” sobie życie i zmodyfikować dostarczony nam obiekt na potrzeby pojedynczej funkcji — np. odpalić algorytm magicznych piątek aby znaleźć k-tą wartość w tablicy, przy okazji zaburzając jej kolejność — i w ten sposób popsuć sobie coś innego na przeciwnym końcu programu.

Niezmiennicze obiekty (i ogólnie unikanie efektów ubocznych) zdejmuje z programisty konieczność sprawdzania i pamiętania o stanie globalnym, co w sytuacji gdy tego stanu zaczyna być naprawdę sporo, oznacza spore ułatwienie. Wystarczy zobaczyć deklarację, żeby wiedzieć, że korzystanie z tego obiektu jest bezpieczne, nie trzeba analizować całości kodu.

Dopiero na drugim miejscu jest ułatwianie życia kompilatorowi — niezmienniczość umożliwia bardziej agresywną optymalizację, zwłaszcza w przypadku aplikacji wielowątkowych.

Są sytuacje, w których zmienność jest potrzebna, ale prawie zawsze to próba oszczędzenia sobie kilku minut, która potem będzie nas kosztować kilka godzin. Tak naprawdę prawie nigdy nie potrzebuje się faktycznie zmiennych obiektów w sytuacjach praktycznych innych niż embedded (i operacje na „gołych bitach”).

TI
@Althorion: Wydaje mi sie, że jeszcze za mało wieczorów zarwalem, bo ktoś gdzieś coś mi zmienił dlatego uważam to za paranoję. Z tego co mówisz to Ty odwracasz te proporcje czyli rzadko kiedy stosujesz mutowalnosc. A z ciekawości kiedy to robisz ?
Althorion
Jak coś musi być mutowalne. Trudno tu trochę o przykłady, ale spróbuję… Ogólnie — mutowalne powinny być te własności, które są w oczywisty sposób zmienne, bo cała idea trzymania tej zmiennej służy do śledzenia stanu. Np. obecna pozycja obiektu, kiedy ten obiekt jest ruchomy (i wszyscy wiedzą, że jest ruchomy). A kiedy nie? Np. lista tych obiektów, jeśli ta jest tworzona raz (np. wczytujemy je i ich nam ani nie przybywa, ani nie ubywa).
TI
@Althorion: Tak, to sie zgadzam, w sumie bardziej zastanawiałem się idąc warstwami, ale skoro mowisz, że starasz się robić obiekty niemutowalne, to pewnie encje bazodanowe, dto, encje biznesowe i obiekty dla widoku starasz się robić immu, chyba że są jakieś przeciwskazania.
TI
@Althorion: Jeszcze pytanie z przykladem, mam listę samochodow wyciągnięta, która już jest w postaci encji biznesowych. Teraz logika biznesowa mowi, że co godzinę przeceniamy 3 losowe samochody. W takim przypadku przy niemutowalnosci usuwamy 3 przecenione auta z listy i dodajemy na ich miejsce te same auta ze zmienionym stanem, zamiast zmienić stan obiektów na liście?
Althorion
Tak, staram się tak robić, chyba że są jakieś przeciwwskazania — i właśnie znalazłeś jedno dobre przeciwwskazanie. Mianowicie, ceną za niemutowalność samochodu jest rezygnacja z niemutowalności listy samochodów. A że naturalna jest zmiana samochodu (bo to on zmienia cenę), a nienaturalna jest zmiana listy (bo z listą się nic nie dzieje — wciąż są na niej fizycznie te same samochody, a zmiana to nieoczywisty szczegół implementacyjny), to to jest przykład szkodliwej niemutowalności.
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4707
5

Zalety niemutowalności widać najlepiej jak się zacznie jej używać na ostro... a potem wróci na trochę do projektu w stalym stylu. Nagle się okazuje, że co któryś bugzor wynika z mutacji (w szczególności mutowalne listy i mapy to zuuo). Wcześniej takie przypadki człowiek przyjmował jako naturalną kolej rzeczy.
.
Z tym obciążeniem GC to nie jest do końca tak prosto. Te zmutowane obiekty sa małe i dodatkowo często występują tylko na stosie (escape analysis). Im nowszy JVM tym lepiej.
Z drugiej strony jest coś na rzeczy i raczej należy się narzutu jakiegoś spodziwać (ale nie takiego jak sobie większość wyobraża).

Dodatkowo miałem już taki przykład, że przejście na VAVR (niemutowalne kolekcje) widocznie (choć nie przesadnie) zmniejszyło użycie GC i CPU... bo można było wywalić kilkanaście niepotrzebnych defensywnych kopiowań, które ludzie stosowali jak chcieli się zabezpieczać przed cudami (nie mając kolekcji niemutowalnych).


jeden i pół terabajta powinno wystarczyć każdemu
TI
@jarekr000000: dzięki za podrzucenie pomysłu, spróbuję sprawdzić to na jakimś side projekcie
cerrato
Moderator Kariera
  • Rejestracja:około 7 lat
  • Ostatnio:27 minut
  • Lokalizacja:Poznań
  • Postów:8797
2

TI
cerrato
@ThisIsHowIRoll: i co - było w nim coś ciekawego dla Ciebie? Jest już troszkę jaśniej? :)
TI
@cerrato: Tak! Poruszone zostało tam kilka aspektów z których nie zdawałem sobie sprawy. Jakieś światełko już widzę, ale żeby mieć kaganek przy sobie to trza popisać niemutowalnie ;)
cerrato
Czyli - masz już ogarniętą teorię, teraz czas na praktykę ;)
PI
  • Rejestracja:ponad 9 lat
  • Ostatnio:4 miesiące
  • Postów:2787
0

@scibi92: Immutable Man wypowiedz się

Zobacz pozostały 1 komentarz
PI
nie, dlaczego :D sam tak kiedyś mówił, że koledzy z zespołu tak go nazywali (z tego co pamiętam)
jarekr000000
Oto on Immutable man : Nienaruszalny, jak dolina Rospudy i otulina Parku Krajobrazowego Kabaty(cytat). Niezmienny, jak poziom gry polskiej reprezentacji na mistrzostwach. Nieusuwalny, jak sędzia sądu najwyższego.(@scibi92 nie obraź się, po prostu wyobraziłem sobie dobrą, kinową zapowiedź w stylu generała italii).
S9
@Pinek: przybyłem , zobaczyłem, odpowiedziałem
PI
@scibi92: no to teraz na następnej rozmowie jak mnie zapytają o niemutowalność, to odpowiem "hmm.. coś tam odnośnie ruchania" xD
S9
@Pinek: bo dobra metafora to podstawa :)
S9
  • Rejestracja:ponad 10 lat
  • Ostatnio:6 miesięcy
  • Lokalizacja:Warszawa
  • Postów:3573
3

@ThisIsHowIRoll:
no jak dla mnie stosowanie niemutowalnych obiektów jest bardziej jak zakładanie prezerwatywy przed seksem z kobietą o której przeszłości seksualnej nic nie wiemy
Zalety
1)Brak side effectów. Np. mapujesz jeden obiekt na drugi i nagle okazuje się że nie tego settera co trzeba wywowałes
2)Brak potrzeby robienia niepotrzebnych defensywych kopii (java.util.Calendar, java.util.Date ...)
3)Pomoc dla garbage collectora
4)Polecam przeprowadzić eksperyment:

Kopiuj
   public static void main(String[] args) {
        Map<Person, BigDecimal> salaryMap = new HashMap<>();
        Person person = new Person("Jan", "Nowak");
        salaryMap.put(person, new BigDecimal("15.000"));
        System.out.println(salaryMap.get(person));
        person.lastName = "Dzban";
        System.out.println(salaryMap.get(person));
    }


    static class Person {
        public String firstName, lastName;

        public Person(String firstName, String lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Person person = (Person) o;
            return Objects.equals(firstName, person.firstName) &&
                    Objects.equals(lastName, person.lastName);
        }

        @Override
        public int hashCode() {
            return Objects.hash(firstName, lastName);
        }
    }

A poza tym sądze że settery powinny być zniszczone


"w haśle <młody dynamiczny zespół> nie chodzi o to ile masz lat tylko jak często zmienia się skład"
edytowany 1x, ostatnio: scibi92
TI
@scibi92: zajebisty przykład
elwis
  • Rejestracja:ponad 18 lat
  • Ostatnio:4 dni
2

Jak się zaczyna przygodę z programowaniem można tego nie rozumieć, bo się nie przerobiło koszmaru skutków ubocznych. To się tak wydaje, że jak się będzie uważać to skutki uboczne można ogarnąć. Prędzej czy później problemy się pojawią. Dodatkowo jak się programuje bez skutków ubocznych myślenie o kodzie jest prostsze, łatwiej robi się refactor, bo wiesz że kawałek kodu możesz przenieść w inne miejsce, bo nie było powiązane z resztą kodu w jakiś mało oczywisty sposób. Ludzie tak się boją nieraz refactoru, a w przypadku kodu funkcjonalnego jest to przeważnie bardzo proste. Od jakiegoś czasu programuje w scali i nie używam praktycznie zmiennego stanu (nie licząc plików z których czytam dane) i bardzo sobie to chwalę. Poza tym oczywiście kwestia zrównoleglania, tak... Ale to chyba jeszcze trudniejsze dla pojęcia dla laika.


edytowany 2x, ostatnio: elwis
TI
@elwis: tak jak pisałem, sądzę że za mało wieczorów zarwałem w pracy nad debuggowaniem skutków ubocznych, dlatego bagatelizuje tą zaletę. Spróbuję klepnąć jakiś projekcik na boku i zobaczę jak niemutowalność się sprawdzi.
elwis
To polecam zrobić to od razu w Scali. Moim zdaniem Java się nie nadaje do programowania funkcyjnego, chyba, że coś się zasadniczo zmieniło odkąd kodowałem w niej zawodowo, jakieś 2 lata temu i właśnie wchodziła 8ka, ale nie sądzę, bo to kwestia samego języka - został zaprojektowany do imperatywnego programowania. Scala dobrze wspiera programowanie funkcyjne, ale też pozwala na imperatywne i zmianę stanu. No i pozwala oszczędzić setki linii kodu, bo została tak zaprojektowana, żeby nic nie pisać dwa razy bez potrzeby. W Javie mógłbyś nie dostrzec pełni uroku. ;)
danek
  • Rejestracja:ponad 10 lat
  • Ostatnio:7 miesięcy
  • Lokalizacja:Poznań
  • Postów:797
0

Czy zapis czegoś do bazy danych jest side-effectem?


Spring? Ja tam wole mieć kontrole nad kodem ᕙ(ꔢ)ᕗ
Haste - mała biblioteka do testów z czasem.
S9
  • Rejestracja:ponad 10 lat
  • Ostatnio:6 miesięcy
  • Lokalizacja:Warszawa
  • Postów:3573
0

Raczej nie jest (zakładając że robisz to celowo). Może być jesli używasz frameworków typu Hibernate i magi dirty checking...


"w haśle <młody dynamiczny zespół> nie chodzi o to ile masz lat tylko jak często zmienia się skład"
danek
  • Rejestracja:ponad 10 lat
  • Ostatnio:7 miesięcy
  • Lokalizacja:Poznań
  • Postów:797
0

Chodzi mi na przykład o taką sytuacje:

Kopiuj
Either<LeagueError, League> createLeague(String name) {
        return validateName(name)
                .map(League::createWithName)
                .peek(league -> leagueRepository.save(league.getUuid(), league));
    }

Czy da się tego typu operacje zrobić lepiej, zakładając ze z tył siedzi jakas zwykła baza danych?


Spring? Ja tam wole mieć kontrole nad kodem ᕙ(ꔢ)ᕗ
Haste - mała biblioteka do testów z czasem.
lion137
  • Rejestracja:około 8 lat
  • Ostatnio:mniej niż minuta
  • Postów:4927
0
danek napisał(a):

Czy zapis czegoś do bazy danych jest side-effectem?

Generalnie, "side effect", to zmiana czegoś gdzieś. Czyli tak.
Wikipedia


edytowany 1x, ostatnio: lion137
elwis
  • Rejestracja:ponad 18 lat
  • Ostatnio:4 dni
0
danek napisał(a):

Czy zapis czegoś do bazy danych jest side-effectem?

Zasadniczo jest skutkiem ubocznym, bo wyprowadza dane na zewnątrz. Funkcja nie ma skutków ubocznych jeśli poza nią nie wychodzi nic poza wynikiem. Z drugiej strony porzebujemy przechowywać dane, więc zapis do bazy/plików jest konieczny. Na szczęście tego typu skutki uboczne daje się dość dobrze utrzymać w ryzach, bo możemy uznać plik/bazę danych za domniemany parametr i łatwo go kontrolować.


edytowany 3x, ostatnio: elwis
danek
  • Rejestracja:ponad 10 lat
  • Ostatnio:7 miesięcy
  • Lokalizacja:Poznań
  • Postów:797
0

To jak to robić dobrze?

EDIT

W sumie da się uniknąć tego, ale wtedy jako argument trzeba by przekazywać 'całą bazę danych' (albo jakiś jej stan) i zwracać nowy stan bazy. Tylko nie wiem czy jest sens się w to bawić 


Spring? Ja tam wole mieć kontrole nad kodem ᕙ(ꔢ)ᕗ
Haste - mała biblioteka do testów z czasem.
edytowany 1x, ostatnio: danek
DQ
  • Rejestracja:prawie 10 lat
  • Ostatnio:7 miesięcy
  • Postów:141
0

Nie każdy side-effect da się wyeliminować. Tak po prawdzie aplikacja bez side-effectów robiłaby zupełnie nic więc nie powinno się ich eliminować. Z tego powodu niektórzy używają IO (monada, a jakże :)), która pozwala na przeniesienie wykonywania side-effect'u na "end of the world" (nie wiem jak sensownie przetłumaczyć). Przykład biblioteki w pełni funkcyjnej do komunikacji z bazą danych: https://tpolecat.github.io/doobie/docs/01-Introduction.html, http://tpolecat.github.io/presentations/doobie1.html#1. A tutaj trochę o IO: https://typelevel.org/cats-effect/datatypes/io.html, można sobie poczytać i popatrzeć na alternatywne rozwiązania - oba źródła wykorzystują Scalę ale tak czy siak wydaje się być wiedzą, którą warto gdzieś tam mieć.

edytowany 3x, ostatnio: DisQ
danek
dlaczego aplikacja bez side-effectów miała by nic nie robić? Kalkulator nic nie robi?
DQ
Jeśli kalkulator potraktujemy jako zbiór funkcji czystych (podając te same argumenty wejściowe zawsze otrzymamy ten sam wynik, możliwość zastąpienia wywołania funkcji przez wynik - referential transparency) to nie mają one żadnej ingerencji ze światem zewnętrznym. Czyli nie możesz pokazać tego wyniku w konsoli, przesłać requestem HTTP czy zapisać do pliku bo to wszystko są side-effect'y. Żeby program miał sens koniec końców jakiś side-effect musi się zadziać
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 2 godziny
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4707
1

Takie uzupełnienie co do side effectów.

Jak by się uprzeć to funkcji bez efektów ubocznych prawie nie ma.
Można przyjąć, że

Kopiuj
f(x) = 2 

Nie ma efektów ubocznych. Jest czyściutka.

Natomiast taka złośliwa funkcja,

Kopiuj
f(x) = x + x

Na papierze jest czysta. Ale na prawdziwym metalu efekty uboczne występują! Czym, na szczęście, zwykle nie musimy się przejmować, bo logicznie to te efekty (zgadnijcie jakie) są zwykle pomijalne.

Z drugiej strony funkcja:

Kopiuj
f(x) =  DEBUG("bezdziemy mnożyć"); return x *5

Ma side effect na papierze, ale realnie możemy przyjąć, że jest czysta. (ale bajzel... ).

Co do pojęcia programowania funkcyjnego bez side effectów, to go bardzo nie lubię. Właśnie programowanie funkcyjne jest dla mnie programowaniem efektów ubocznych.
W imperatywnym podejściu efekty się zdarzają (nie wiadomo gdzie, nie do końca widać jak).
W fp trzeba je jawnie zaprogramować i zwrócić wyraźnie zaznaczonej postaci (widocznej np. w typie funkcji).
Choć faktycznie przez to sama funkcja staje się czysta, bo efekty są odłożone w jej wyniku.
(stąd to zabawne pojęcie at end of the world, które trochę oddaje charakter takiego pisania).


jeden i pół terabajta powinno wystarczyć każdemu
edytowany 4x, ostatnio: jarekr000000
PI
Jaro, ty weź idź na filozofa :D
PU
x + x może przekroczyć zakres zmiennej? :)
jarekr000000
@pustypawel: nie tylko. To jedna z kilku rzeczy.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.