I podobnie jak w wątku poniżej nie chcę się kłócić, że "funkcyjne jest be", tylko naprawdę nie rozumiem.
Ostatnio mamy wielki trend który głosi, że "Programowanie funkcyjne jest dużo lepsze niż nie-funkcyjne". A w szczególności - lepsze niż wszystko, co imperatywne.
Niech im będzie. Ale dla mnie programowanie funkcyjne jak i argumentacje za nim sa naprawdę trudne do przełknięcia.
Po pierwsze: Dla mnie osobiście jest ono bardzo nieintuicyjne.
Ktoś na reddicie twierdził, że nieintuicyjność FP być może wypływa tylko z tego, że ludzie od początku są szkoleni do myślenia imperatywnego. Gdyby wyrzucić języki imperatywne ze szkół i zastąpić je funkcyjnymi, to myślenie funkcyjne byłoby równie intuicyjne.
Ja natomiast osobiście odnoszę wrażenie, że po prostu informatyka byłaby jeszcze bardziej znienawidzona przez większość uczniów.
Wydaje mi się, że dla człowieka naturalne jest myślenie czasownikami i w kategoriach zmieniającego się stanu.
Imperatywnie: Piszę na kartce zdanie Ala ma kota pod akapitem, który napisałem wcześniej.
Funkcyjnie: Niech k będzie dowolną kartką. k' jest kartką taką, że cały tekst napisany na k' jest identyczny z tekstem napisanym na k, a dodatkowo na k' jest jeszcze zdanie Ala ma kota poniżej jakiegokolwiek innego tekstu.
Nawet pisze się koślawo, co wskazuje, że język naturalny nie powstał do tego rodzaju opisu (bo ludzie tak nie myślą). Językiem ojczystym będzie tu raczej język matematyczny: k' = ιk∈K. tekst(k') = tekst(k) || "Ala ma kota". Tutaj dopiero to wygląda naturalnie; ale ludzie nie posługują się takim językiem na co dzień.
Gdzie tu są problemy? (a) Wyrzucamy czasowniki, bo one ze swej natury modyfikują stan; zamiast tego mamy myśleć deklaratywnie, a więc opisywać co czym jest, a nigdy: co się z czym odbywa. Jednak ludzie myślą czasownikami. (b) Co za tym idzie: Tworzymy nową kartkę. (WTF? Jak piszę na kartce to nie pojawia mi się stosik kartek po każdym napisanym słowie?) Wyrzucenie czasowników wywołuje eksplozję rzeczowników, wskutek czego zdawałoby się jeden i ten sam obiekt nagle opisywany jest wieloma rzeczownikami, a tak w ogóle to są to zupełnie różne obiekty.
Jednak tak nie jest w rzeczywistym życiu, skąd ludzie czerpią intuicje. Krojenie marchewki nie tworzy nowej marchewki. Włączenie telewizora nie powoduje, że w pokoju telewizor włączony zaczyna stać obok identycznego, ale wyłączonego.
Skądinąd zastanawiam się, co by było, gdyby przeprowadzić takie badanie: Wziąć tzw. "zwykłego człowieka" i programistę funkcyjnego i poprosić o wypowiedź na ten sam temat. Zanotować, jaka była u kogo proporcja rzeczowników do czasowników (wyjąwszy "być" i "mieć") oraz proporcja czasowników "być" i "mieć" do innych czasowników. Mam podejrzenia, że programista funkcyjny nawet na co dzień używałby mniej czasowników. Jestem ciekaw, co by z takiego badania wyszło. Bo jeśli moje przypuszczenia są prawdziwe - to jakby potwierdzało to, że bycie programistą funkcyjnym wymaga bardzo głębokich zmian w intuicji.
Po drugie: Współdzielony zmieniający się stan bywa wygodny!!
Na JPP na studiach był prikaz, by napisać interpreter wymyślonego przez siebie języka w Haskellu. To mnie zmusiło, by tego Haskella wreszcie spróbować.
Jakoś nie byłem zachwycony.
Wymyśliłem sobie język (imperatywny, ale z kilkoma nowatorskimi pomysłami). Tak - jest monada State
. Pomocna przy takiej zabawie. Wśród licznych obiektów odpowiadających za stan mamy np. listę scope'ów. W moim języczku akurat nie było eksplozji możliwych dowiązań do tego samego scope'u, ale wciąż jednak pod pewnymi warunkami można się było dostać do tego samego scope'u z kilku różnych miejsc.
To natychmiast zaczęło wymagać, by każdy scope miał swój id. Scope nie mógł być dowiązany bezpośrednio do nazwy zmiennej - nie, nazwa zmiennej miała przechowywać id swojego scope'a.
Zamiast zatem prostego przejścia ze zmiennej do scope'a: Pobranie id, pobranie scope'a o tym id ze stanu przechowywanego w monadzie, zrobienie coś z tym scope'em, zapisanie nowego stanu... I tak jednolinijkowiec stawał się co najmniej trójlinijkowcem. (FP ma zmniejszać boilerplate w porównianiu do obiektówki)
I co za tym jeszcze idzie: Trzeba nagle eksplicite w ogóle deklarować tę listę scope'ów! W języku imperatywnym w ogóle takiej listy bym nie stworzył. Zamiast tego bym po prostu dołączał odpowiednie referencje.
(I co jeszcze za tym idzie, choć to już nie problem z mojego projektu, bo w moim języku z innych przyczyn i tak byłoby to niemożliwe: Problemy z automatycznym zarządzaniem pamięcią. Wyobrażam sobie, że gdybym pisał interpreter w nawet tej nieszczęsnej Javie (interpreter normalniejszego języka niż to, co wymyśliłem), to miałbym garbage collecting za darmo. Ginie ostatnia zmienna, która dowiązywała do jakiegoś scope'u? Brak jest dowiązań do scope'u, ginie i scope. W Haskellu? Ponieważ musi być centralny słownik wszystkich scope'ów, to dowiązanie do scope'a jest i wyciek pamięci gotowy.)
Co więcej: to wymuszało, by pojedyczne funkcje były naprawdę bardzo małe i robiły naprawdę tylko jedną rzecz. Dlaczego? Bo po zapisaniu zmian do monady trzeba... Pobrać dane z monady ponownie. Jeśli tego nie zrobię, to będę operował na nieaktualnych danych. To wymusza wyrzucanie każdej operacji pobierz/zapisz do osobnej funkcji, bo inaczej funkcja zaczyna być nieznośna. (Tak, pewnie ktoś zaraz powie, że "to dobrze bo i w obiektówce funkcje winny być jak najmniejsze - ciesz się, programowanie funkcyjne wymusza na tobie, co i tak powinieneś robić.)
Chodzi mi o to, że - jak dla mnie - w FP trzeba bardzo pilnować, by się stan nie rozsynchronizował. Ciągłe tworzenie nowych i jeszcze nowych obiektów łatwo może doprowadzić do tego, że skądś dojdziemy do nieaktualnych danych.
A teraz teoretycznie: Gra. Chociażby ta, którą dłubię. To wydaje się być po prostu w opór imperatywne: Jeden stworek wali drugiego więc drugi traci 20 HP. Chcę wykorzystać fakt, że można modyfikować stan globalny! Bo teraz za darmo mam już to, że to zmniejszenie się HP jest widoczne zewsząd: z listy stworków drużyny pojedynczego gracza, ze stworka, który właśnie zadał cios (referencja o wdzięcznej nazwie opponent
), wszytkie efekty które zależą od HP też będą miały dostęp do aktualnego HP... W Haskellu nie miałbym tego. Gdybym po prostu zrobił dowiązania, to te dowiązania zdezaktualizowałyby się z chwilą zadania ciosu. A więc znów - centralny rejestr wszystkich stworków, jakieś sztuczne id tych stworków pewnie (aktualny przeciwnik nie jest zatem stworkiem tylko jakąś dziwną liczbą, na podstawie której trzeba dopiero pobrać stworka), i pewnie jeszcze to samo z wieloma innymi rzeczami, gdzie obecnie wykorzystuję dowiązania.
Globalny stan jest be - ale jak stworek oberwie to ja naprawdę chcę, żeby ze wszystkich innych miejsc w kodzie było widać zmniejszone HP!! A przecież w myśl paradygmatu funkcyjnego to jest właśnie tragedia, jeśli zmiana w jednym miejscu kodu propaguje się nagle do innych miejsc w kodzie. Tyle że dla mnie wyrzucenie takich propagacji wymusza komplikowanie kodu, jak opisałem na przykładach wyżej.
Jednak
Uznaję, że powyższe problemy mogą po prostu wynikać z tego, że nigdy nie nauczyłem się porządnie pisać funkcyjnie. Może po prostu próbuję przenosić praktyki, nawyki i intuicje z jęz. imperatywnych do jęz. funkcyjnych, widzę, że one jakoś nie pasują, więc odrzucam Haskella podczas gdy powinienem odrzucić te nawyki - czyli oduczyć się wszystkiego, co już umiem, i uczyć się na nowo.
Może zatem dla własnego dobra trzeba by się zmusić do pisania fukncyjnego, i powrócenia do tematu za pół roku albo i rok nawet - może po takim ćwiczeniu też bym nagle zaczął dostrzegać same zalety FP i już nigdy bym nie chciał z własnej woli pisać imperatywnie.
Nie za bardzo widzę, jak to zrobić w praktyce. W pracy mam Pythona na razie - nie chcę wykorzystywać zadań z pracy do nauki paradygmatu, z którym na razie nie za bardzo mi po drodze. Ucierpiałaby praca. Uczyć się tego sam dla siebie? Sam dla siebie piszę grę w C# - jak zacznę przepisywać ją na Haskella to chyba nigdy jej nie zrobię. Tzn będzie to może interesujące ćwiczenie pod wzgl. nauki Haskella, ale raczej będzie wymagało porzucenia marzeń o ukończeniu jej.
Tak, niby na upartego w C# DA SIĘ pisać funkcyjnie, ale znów to samo... zbyt wielkie skupienie na tym, JAK to mam robić doprowadzi do tego, że tego nie zrobię. Tym bardziej, że obecnie dostrzegam tylko upierdliwości na myśl o porzuceniu wszelkich side-effectów.
Uczyć się funkcyjnego jako trzecie zadanie, obok pracy i powyższej gry? Sorry.. doba ma 24h.
A jednak przejść nad tym do porządku dziennego nie mogę. Za wiele ludzi śpiewa peany na cześć FP. Naprawdę może być tak, że nie ucząc się go, krzywdzę wyłącznie samego siebie. I z własnego wyboru pozostaję niekompetentny.
W zasadzie nie wiem, co teraz z tym zrobić.
Freja DracoUznaję, że powyższe problemy mogą po prostu wynikać z tego, że nigdy nie nauczyłem się porządnie pisać funkcyjnie.
Może się naucz, napisz jakiś projekt w języku czysto funkcyjnym i wtedy się wypowiedz. Co myślisz?kmph