Wyobraź sobie laboratorium w Parku Jurajskim, w którym każdy eksperyment na DNA dinozaura daje zawsze ten sam rezultat przy tych samych danych wejściowych. Nie ma żadnych efektów ubocznych - żaden dinozaur nie ucieka, żaden system bezpieczeństwa nie zostaje przypadkowo wyłączony. Dokładnie tak działa programowanie funkcyjne (FP) - to paradygmat, w którym kod zachowuje się jak kontrolowany eksperyment naukowy.
W programowaniu imperatywnym mówimy komputerowi jak ma coś zrobić krok po kroku. W programowaniu funkcyjnym opisujemy co chcemy osiągnąć, używając funkcji jako podstawowych jednostek budulcowych.
1// Podejście imperatywne - krok po kroku
2const dinosaurs = ['T-Rex', 'Velociraptor', 'Triceratops', 'Stegosaurus'];
3const carnivores = [];
4for (let i = 0; i < dinosaurs.length; i++) {
5 if (dinosaurs[i] === 'T-Rex' || dinosaurs[i] === 'Velociraptor') {
6 carnivores.push(dinosaurs[i]);
7 }
8}
9
10// Podejście funkcyjne - deklaratywne
11const isCarnivore = (name) => ['T-Rex', 'Velociraptor'].includes(name);
12const carnivoresFP = dinosaurs.filter(isCarnivore);Czysta funkcja to kontrolowany eksperyment - przy tych samych danych wejściowych zawsze zwraca ten sam wynik i nie powoduje żadnych efektów ubocznych.
1// CZYSTA FUNKCJA - zawsze ten sam wynik
2function calculateDangerLevel(weight, speed, aggression) {
3 return (weight * 0.3) + (speed * 0.3) + (aggression * 0.4);
4}
5
6// Ta sama funkcja wywolana 1000 razy z tymi samymi argumentami
7// zawsze zwroci ten sam wynik
8calculateDangerLevel(8000, 40, 95); // zawsze 2465
9calculateDangerLevel(8000, 40, 95); // zawsze 2465
10
11// NIECZYSTA FUNKCJA - modyfikuje dane zewnetrzne
12let totalDinosaurs = 0;
13function addDinosaur(name) {
14 totalDinosaurs++; // efekt uboczny - zmienia zmienna zewnetrzna!
15 return { name, id: totalDinosaurs };
16}
17// Kazde wywolanie daje inny wynik, nawet z tym samym argumentem
18addDinosaur('Rex'); // { name: 'Rex', id: 1 }
19addDinosaur('Rex'); // { name: 'Rex', id: 2 } - inny wynik!Niemutowalność to zasada, że raz utworzone dane nigdy nie są zmieniane. Zamiast modyfikować istniejący obiekt, tworzymy nową kopię ze zmianami. To jak tworzenie nowej sekwencji DNA zamiast modyfikowania istniejącej - bezpieczniej i bardziej przewidywalnie.
1// MUTACJA - niebezpieczna (zmiana oryginalnego obiektu)
2const dinosaur = { name: 'Rex', health: 100 };
3dinosaur.health = 80; // Oryginaly obiekt zostal zmieniony!
4
5// NIEMUTOWALNOSC - bezpieczna (nowy obiekt)
6const dinosaurSafe = { name: 'Rex', health: 100 };
7const injuredDinosaur = { ...dinosaurSafe, health: 80 };
8// dinosaurSafe wciaz ma health: 100 (niezmieniony)
9// injuredDinosaur ma health: 80 (nowy obiekt)W JavaScript funkcje są wartościami pierwszoklasowymi (first-class citizens) - możesz je przypisywać do zmiennych, przekazywać jako argumenty i zwracać z innych funkcji.
1// Funkcja przypisana do zmiennej
2const classify = (dino) => dino.diet === 'carnivore' ? 'dangerous' : 'safe';
3
4// Funkcja jako argument
5const dinosaurs = [
6 { name: 'Rex', diet: 'carnivore' },
7 { name: 'Brachio', diet: 'herbivore' },
8];
9const dangerous = dinosaurs.filter((dino) => classify(dino) === 'dangerous');
10
11// Funkcja zwracajaca funkcje
12function createValidator(minWeight) {
13 return (dino) => dino.weight >= minWeight;
14}
15const isHeavyDino = createValidator(5000);
16isHeavyDino({ weight: 8000 }); // true
17isHeavyDino({ weight: 200 }); // false