Co to jest programowanie funkcyjne?
10 mins read

Co to jest programowanie funkcyjne?

Gdy mówimy „programowanie funkcyjne”, mamy na myśli pewien paradygmat, który wykorzystuje funkcje do konstruowania programów. Funkcje te są tworzone poprzez ich komponowanie i stosowanie. Często program składa się tylko z kilku funkcji, takich jak ciąg znaków lub lista. Innymi słowy, jest to styl programowania, który jest zarówno strukturalny, jak i elastyczny. Styl ten jest często bardziej wydajny w przypadku dużych projektów, a może być skuteczny w przypadku mniejszych.

John Backus

W 1977 roku Backus otrzymał od ACM nagrodę Turinga, która jest uważana za Nobla w dziedzinie informatyki. Wygłosił wówczas na dużej konferencji informatycznej przemówienie zatytułowane „Can Programming Be Liberated from the von Neumann Style? A Functional Style and Its Algebra of Programs”. Chociaż pomysły Backusa miały już kilkadziesiąt lat, pobudziły zainteresowanie programowaniem funkcjonalnym, dziedziną, która stała się bardzo ważna w przemyśle komputerowym.

Po ukończeniu doktoratu Backus spędził następne 15 miesięcy w IBM Research, gdzie pracował nad koncepcją programowania funkcjonalnego. W tym okresie napisał prototypy w języku LISP, języku McG oraz języku podobnym do ISWIM. Badał semantykę języków programowania i opublikował raport o własności Churcha-Rossera, własności zamkniętych języków aplikacyjnych. Rodzina Backusów pozostała następnie w Stanach Zjednoczonych, aż Backus zmarł w swoim domu w Ashland w stanie Oregon.

W pierwszych latach dwudziestych Backus chodził do szkoły średniej w Filadelfii i ukończył Columbia University w 1949 roku. Początkowo został umieszczony na kursie przedmedycznym, ale został wysłany na specjalistyczny kurs inżynierski na Uniwersytecie w Pittsburghu. Po dwóch latach zapisał się na Uniwersytet Columbia, gdzie studiował matematykę. Podczas stażu zdiagnozowano u niego guza mózgu i założono mu metalową płytkę. Backus później wymienił płytkę na zaprojektowaną na zamówienie, która była bardziej elastyczna.

Clojure

Pierwszą istotną różnicą między Clojure a Javą jest sposób, w jaki modelują zmienne, stan i przechowywanie. Podczas gdy model Javy jest oparty na pudełku, Clojure traktuje pamięć i stan jako abstrakcyjny typ danych. W rezultacie, w Clojure nie ma modelu „pudełkowego”. Raczej zmienne i wartości są reprezentowane przez ich wartość, a nie przez nazwę. Te różnice sprawiają, że Clojure jest bardziej wydajnym językiem do pisania aplikacji.

Jedną z największych wad Javy jest fakt, że w Clojure nie można używać operatorów. Jednak rekurencja jest funkcjonalną alternatywą dla mutacji, ponieważ iteruje nad kolekcją i buduje wynik. Ta metoda wprowadza nowy zakres do kodu, a po zakończeniu rekurencji Clojure wraca do zakresu globalnego. Ułatwia to korzystanie z Clojure, ale również utrudnia zapobieganie błędom runtime.

Ponadto Clojure posiada unikalną cechę: interfejs seq. Abstrahuje on konstrukcję listy i zapewnia sposób na rozszerzenie funkcji bibliotecznych na dowolną sekwencyjną strukturę danych. Wszystkie struktury danych Clojure mogą dostarczać seq. Są one podobne do iteratorów w innych językach, ale są niezmienne i trwałe. Seqs zapewniają funkcję first, która zwraca pierwszy element sekwencji, oraz funkcję rest, która zwraca resztę sekwencji.

Styl FP Clojure’a

W przeciwieństwie do innych języków programowania funkcyjnego, Clojure nie posiada statycznego systemu typowania i używa zwykłych danych dla zmiennych. Może to prowadzić do błędów, jeśli spróbujesz użyć zmiennej jako funkcji lub gdy obiekt odbierający nie może obsłużyć funkcji. Brak statycznego typowania ułatwia załatwianie spraw w Clojure, ale także utrudnia pisanie kodu bez zapobiegania błędom runtime.

Jako przykład tego, jak różni się to od stylu FP Javy, Clojure ma wbudowaną klasę Atom, która jest podobna do AtomicReference Javy, ale ma więcej funkcji. Atom jest odniesieniem do całej niezmiennej wartości. Aby zmienić stan atomu, programista musi napisać funkcję, która wytworzy nowy stan. Ten mechanizm zapewnia, że wartość zmienia się tylko raz, pozwalając programiście pisać funkcje, aby zmienić jej stan w razie potrzeby.

Składnia Scali i Clojure jest podobna, choć Scala może być łatwiejsza dla początkujących. Podczas gdy Scala jest bardziej znanym językiem dla programistów Java, składnia w Clojure może być nieznana. Jednak oba języki zapewniają solidne rozwiązania dla wielu problemów. Będziesz musiał zdecydować, który z nich będzie działał najlepiej dla twoich potrzeb. Clojure jest lepszym wyborem, jeśli pracujesz nad dużymi, złożonymi aplikacjami.

Funkcje pierwszej klasy

Funkcje pierwszej klasy są podstawowym budulcem programowania funkcjonalnego. Funkcje te mogą być używane jako parametry innych funkcji, zwracane z innej funkcji i przypisywane do zmiennych. Dzięki temu możesz je wywoływać kiedy tylko chcesz. Jeśli potrzebujesz dostępu do funkcji z kilkoma parametrami, możesz użyć do tego zagnieżdżonej pętli. Funkcje pierwszej klasy to świetny sposób na organizowanie kodu w komponenty wielokrotnego użytku. Pozwalają również pisać bardziej złożone programy bez obaw o to, jak utrzymać kod.

Istnieje kilka różnych typów funkcji pierwszej klasy. Pierwszym z nich jest funkcja map, która przyjmuje listę jako argument i zwraca nową listę d. Drugim typem jest filtr, który stosuje funkcję do listy elementów. Funkcja ta działa z dowolną listą. Możesz również użyć funkcji map do filtrowania lub redukcji list, w zależności od charakteru danych. W obu przypadkach lista, którą przekazujesz do funkcji map, musi mieć nazwę.

Język funkcjonalny obsługuje również funkcje wyższego rzędu. Funkcje wyższego rzędu mogą przyjąć inną funkcję jako argument i zwrócić nową funkcję. Funkcje wyższego rzędu nie są funkcjami pierwszej klasy, ale są uważane za funkcje „wyższego rzędu”. Ale funkcje pierwszej klasy są kluczem do funkcji wyższego rzędu. W programowaniu funkcjonalnym należy unikać używania funkcji w stylu Excela dla funkcji wyższego rzędu, ponieważ funkcje te nie są obsługiwane przez język funkcjonalny.

Przezroczystość referencyjna

Przezroczystość referencyjna i nieprzezroczystość są właściwościami programów komputerowych. Wyrażenia przezroczyste referencyjnie pozwalają na zamianę ich wartości bez zmiany zachowania programu. Jest to podstawa programowania funkcyjnego. Stosuje się ją, aby język był bardziej elastyczny i możliwy do utrzymania. Należy jednak zdawać sobie sprawę z ograniczeń tego podejścia przed wdrożeniem go do własnych programów. Czytaj dalej, aby poznać niektóre zalety tego stylu programowania.

Przejrzystość referencyjna to właściwość notacji, która pozwala bezpośrednim wyrażeniom składowym mieć istotne znaczenie. Oznacza to, że wszelkie wyrażenia wewnątrz tej samej funkcji mają to samo znaczenie. W rezultacie przejrzystość referencyjna oznacza, że wyrażenia podrzędne mogą być zamieniane i mieć jednakowe wyniki. Termin przejrzystość referencyjna jest używany w wielu językach programowania, w tym w C i Pythonie. W programowaniu funkcjonalnym termin przezroczystość referencyjna ma różne znaczenia. Celem przezroczystości referencyjnej jest zapewnienie, że ponowne użycie wyniku funkcji nie jest ograniczone do kontekstu większego wyrażenia.

Funkcje przezroczyste referencyjnie mogą być napisane w dowolnym języku programowania. Python, Scheme, Pascal i C są przykładami takich funkcji. Ta sama funkcja może mieć wiele argumentów i dawać różne wyniki, ale jej główną zaletą jest to, że nie zależy od wartości argumentów. Funkcja, która nie ma efektów ubocznych, nie jest referencyjnie przezroczysta. Funkcja nie jest referencyjnie przejrzysta, jeśli ma efekty uboczne lub jest wywoływana więcej niż raz.

Czyste funkcje

Paradygmat programowania czysto funkcyjnego traktuje wszystkie obliczenia jako ocenę funkcji matematycznych. Ten paradygmat jest szeroko stosowany w inżynierii oprogramowania, uczeniu maszynowym i informatyce. Ten paradygmat jest potężnym sposobem na pisanie programów i szybsze tworzenie aplikacji. Ten paradygmat jest bardzo przydatny podczas pracy z dużymi bazami danych i złożonymi symulacjami. Jest on również niezwykle elastyczny. Na przykład pozwala zaprojektować algorytmy, które działają bez konieczności korzystania z zewnętrznych bibliotek. Dalej, paradygmat ten pozwala na tworzenie aplikacji bez martwienia się o to, czy zostaną one uruchomione w świecie rzeczywistym.

Inną kluczową zaletą programowania funkcjonalnego jest stosowanie czystych funkcji. Są to programy, które nie mają żadnych efektów ubocznych, nie polegają na zewnętrznym stanie i zawsze produkują to samo wyjście. W rezultacie, są one łatwiejsze do zrozumienia. A ponieważ są tak elastyczne, czyste funkcje mogą być stosowane w każdym języku programowania. Niezależnie od tego, czy piszesz aplikację w Javie, czy skrypt w Pythonie, to podejście jest świetnym wyborem dla początkujących.

Przy korzystaniu z programowania funkcjonalnego ważne jest, aby pamiętać, że szukasz matematycznego rozumowania. A najlepszym sposobem, aby to zrobić, jest użycie czystych funkcji, a nie funkcji JavaScript. Funkcje JavaScript, na przykład, pozwalają wysłać żądanie AJAX do strony internetowej, odczytać dane wejściowe i inne. Ale funkcja matematyczna jest znacznie bardziej ograniczona. Definiuje ona związek pomiędzy wartościami argumentów i wartością zwracaną. Te dwa pojęcia są powszechnie określane jako dziedzina i zakres funkcji, lub domena.