Kilka lat temu mój kolega z pracy, developer pythona, który to uwielbiał
czytać książki techniczne rozwijając przy tym swój wachlarz umiejętności
zapytał mnie, osoby która zajmowała się JavaScript, jak to w “moim”
języku wyglądają monady.
Flame
Pierwsze co pomyślałem, to że znowu zaczyna się flame - czyli kolejna kłótnia, nad wyższością swojego języka programowania. Nie lubię brać udziału w takich dyskusjach, ponieważ zwykle poza zwiększeniem poziomu stresu niczego wartościowego nie doświadczymy.
Niestety zawiodłem, ponieważ nie znałem odpowiedzi na jego pytania.
Wtedy nie wiedziałem czym się charakteryzuje programowanie funkcyjne, tym samym coś takiego jak monady w JavaScript dla mnie nie istniały.
Generalnie po kilkudziesięciu minutach researchu dowiedziałem się, że monady to takie “idiotoodporne” funkcje. Gama metod biblioteki jQuery jest dobrym przykładem, które to nigdy (a raczej rzadko kiedy) rzucą wyjątku. Z takim myśleniem zostałem przez kolejne 2-3 lata.
Po 2 latach bycia niedoinformowanym…
Pewnego dnia mój kolega Michał Kawalec, zaprosił mnie na Monadic Warsaw. Mowa tu dokładnie o 6-tej edycji. Monadic to organizacja, której przyświeca cel rozmawiania o programowaniu funkcyjnym. Postanowiłem, że z miła chęcią się odwdzięczę i opowiem o monadach w JavaScript - doczytam, przygotuję się i wyjdę na scenę.
Poczytałem, przestudiowałem.
Zrobiłem slajdy https://piecioshka.github.io/slides-monads-in-javascript/.
Jestem przygotowany - do dzieła zatem!
Wychodzę na scenę
Przyszedłem na spotkanie. Pełna sala.
Świetnie jest widzieć tylu ludzi, którzy poza swoimi codziennymi obowiązkami przychodzą na spotkania i się rozwijają.
Po wysłuchaniu pierwszej prezentacji poprowadzonej przez Bartosza Milewskiego, który to wyjaśniał teorię kategorii, dowiedziałem się zdecydowanie więcej o monadach niż przypuszczałem.
Dowiedziałem się, że monada to konstruktor typu M z dwiema operacjami:
unit, która opakowuje zwykłą wartość w M, oraz bind, która przekazuje
opakowaną wartość do funkcji zwracającej kolejne M. Wynik bind to znów
M, a nie M zagnieżdżone w M, dzięki czemu operacje można łączyć w łańcuch.
jQuery to nie jest monada
Biblioteka jQuery, o której wcześniej pisałem nie jest więc przykładem na realizację monad w JavaScript.
Poniżej jeden z slajdów z mojej prezentacji:

Źródła slajdów dostępne tu: https://github.com/piecioshka/slides-monads-in-javascript
Podczas swojego researchu przed prezentacją i kompletowaniem źródeł, z których korzystałem przy tworzeniu slajdów natknąłem się na post https://www.quora.com/Is-jQuery-a-monad.
W pierwszym komentarzu mój poprzednik na scenie wyjaśnił, że wszyscy, którzy uważają jQuery za monadę, żyją w błędzie.
jQuery nie jest monadą, ponieważ funkcja $() sama spłaszcza zagnieżdżenia:
$($($('body'))) jest tym samym co $('body').
Przez takie zachowanie nie da się zapisać unit i bind dla jQuery tak, żeby
spełniały prawa monad opisane niżej.
Podczas mojego researchu dowiedziałem się o 3 aksjomatach, które muszą zostać spełnione, aby można było coś nazwać monadą.
3 prawa monad
Przetłumaczyłem opis znajdujący się w projekcie https://github.com/piecioshka/my-monad#the-monadic-laws na język polski:
- monada jest wraperem na inny typ
- wszystkie funkcje monad muszą posiadać funkcję do wrapowania samych siebie o inne typy danych
- wszystkie monady muszą być zdolne aby zasilać wartością, które wrapują, inne funkcje, dopóki one nie zwrócą kolejnej monady
Te punkty opisują jednak budowę monady, a nie jej prawa. Właściwe prawa to trzy
równości, które muszą spełniać operacje unit i bind:
- lewa tożsamość:
unit(a).bind(f)daje to samo cof(a) - prawa tożsamość:
m.bind(unit)daje to samo com - łączność:
m.bind(f).bind(g)daje to samo com.bind(x => f(x).bind(g))
W czym monady pomagają?
Monady mają pomóc w programowaniu i to robią.
Najpopularniejszym przykładem czegoś bardzo podobnego do monady jest obiekt
Promise w JavaScript. Ścisłą monadą nie jest, bo then sam spłaszcza
zagnieżdżone obietnice i łączy w sobie dwie operacje (map i bind).
To dzięki niemu piszemy kod asynchronicznych w sposób bardzo czytelny. Przykład:
function makeSomething(value) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(value), 1000);
});
}
Promise.resolve(3)
.then(makeSomething)
.then((value) => {
console.debug(value);
});
Podsumowanie
Temat monad jest bardzo szeroki. Nie opisałem jak samemu stworzyć implementację
chociażby Identity Monad, ze względu na to, że istnieje już wiele miejsc
gdzie można takie implementację zobaczyć.
Na swoim profilu na GitHubie stworzyłem projekt, z implementacją monad w JavaScript. Zapraszam do odwiedzenia przykładowych testów: https://github.com/piecioshka/my-monad
Aby jeszcze bardziej zapoznać się monadami i ich różnymi typami zapraszam do 11 slajdu mojej prezentacji na Monadic Warsaw https://piecioshka.github.io/slides-monads-in-javascript/#11. Jest tutaj lista podstawowych typów monad jakie występują w przyrodzie.
Bibliografia
Na zakończenie warto zapoznać się ze źródłami, z których czerpałem wiedzę:
- Monads & Gonads - Douglas Crockford (GoogleTechTalks)
- Monads in JavaScript
- Brian Beckman: Don’t fear the Monad
- Monads and Objects
- So simple MONAD implementation
- Step by step into Monads
- Use monads in JavaScript without needing to know how they work
- jQuery is not a monad
Cya! 👋