Czemu Pan powiedział, że immutability jest dobre ?

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.

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.

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”).

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).

0

@scibi92: Immutable Man wypowiedz się

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:

   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

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.

0

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

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...

0

Chodzi mi na przykład o taką sytuacje:

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?

0
danek napisał(a):

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

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

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ć.

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ć 

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ć.

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

f(x) = 2 

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

Natomiast taka złośliwa funkcja,

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:

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).

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