Aby uszanować szybkich czytelników, najpierw zaczynam od precyzyjnej definicji, kontynuuję szybkie wyjaśnienia w języku angielskim, a następnie przechodzę do przykładów.
Oto nieco zwięzła i precyzyjna definicja nieco przeredagowana:
Monada (w informatyce) jest formalnie mapa, która:
wysyła każdy typ X
danego języka programowania do nowego typu T(X)
(zwanego „typem T
obliczeń z wartościami w X
”);
wyposażony w regułę tworzenia dwóch funkcji formularza
f:X->T(Y)
i g:Y->T(Z)
funkcji g∘f:X->T(Z)
;
w sposób asocjatywny w oczywistym sensie i jedności w odniesieniu do danej wywoływanej funkcji jednostkowej pure_X:X->T(X)
, którą należy traktować jako przyjmowanie wartości do czystego obliczenia, które po prostu zwraca tę wartość.
Tak więc w prostych słowach monada jest regułą przekazywaną z dowolnego typu X
na inny typT(X)
oraz regułą przekazywaną z dwóch funkcji f:X->T(Y)
i g:Y->T(Z)
(którą chciałbyś skomponować, ale nie możesz) do nowej funkcjih:X->T(Z)
. Który jednak nie jest kompozycją w ścisłym sensie matematycznym. Zasadniczo zajmujemy się kompozycją funkcji „zginania” lub redefiniowaniem sposobu, w jaki funkcje są tworzone.
Dodatkowo, wymagamy reguły komponowania monady, aby spełnić „oczywiste” matematyczne aksjomaty:
- Łączność : Tworzenie
f
się g
, a następnie z h
(od zewnątrz), powinny być takie same jak komponować g
z h
a następnie f
(od wewnątrz).
- Właściwość Unital : Komponowanie
f
z funkcją tożsamości po obu stronach powinno dać f
.
Ponownie, w prostych słowach, nie możemy po prostu zwariować, zmieniając skład naszej funkcji tak, jak lubimy:
- Najpierw potrzebujemy asocjatywności, aby móc np. Skomponować kilka funkcji z rzędu
f(g(h(k(x)))
, i nie martwić się o określenie par funkcji tworzących porządek. Ponieważ reguła monady określa tylko, jak skomponować parę funkcji , bez tego aksjomatu musielibyśmy wiedzieć, która para składa się jako pierwsza i tak dalej. (Należy pamiętać, że różni się od właściwości komutatywności, f
z którą skomponowane g
były takie same, jak g
z f
, która nie jest wymagana).
- Po drugie, potrzebujemy jednolitej własności, czyli po prostu powiedzieć, że tożsamości składają się w sposób trywialny tak, jak się ich spodziewamy. Możemy więc bezpiecznie refaktoryzować funkcje za każdym razem, gdy można wyodrębnić te tożsamości.
Tak więc w skrócie: monada jest regułą rozszerzenia typu i tworzenia funkcji spełniających dwa aksjomaty - asocjatywność i właściwość jedności.
W praktyce chcesz, aby monada została zaimplementowana dla Ciebie przez język, kompilator lub środowisko, które zajmą się tworzeniem dla Ciebie funkcji. Możesz więc skupić się na pisaniu logiki funkcji, zamiast martwić się o to, jak zostanie wykonana ich realizacja.
To w gruncie rzeczy w skrócie.
Jako profesjonalny matematyk wolę unikać nazywania h
„kompozycją” f
i g
. Ponieważ matematycznie tak nie jest. Nazywanie go „kompozycją” niepoprawnie zakłada, że h
jest to prawdziwa kompozycja matematyczna, a tak nie jest. Nie jest to nawet jednoznacznie określone przez f
i g
. Zamiast tego jest to wynikiem nowej „zasady komponowania” naszych monad. Który może całkowicie różnić się od faktycznego składu matematycznego, nawet jeśli ten drugi istnieje!
Aby uczynić go mniej suchym, pozwól mi zilustrować go przykładem, który adnotuję za pomocą małych sekcji, abyś mógł przejść od razu do rzeczy.
Zgłaszanie wyjątków jako przykłady Monady
Załóżmy, że chcemy skomponować dwie funkcje:
f: x -> 1 / x
g: y -> 2 * y
Ale f(0)
nie jest zdefiniowany, dlatego zgłaszany e
jest wyjątek . Jak zatem zdefiniować wartość składu g(f(0))
? Oczywiście ponownie rzuć wyjątek! Może to samo e
. Może nowy zaktualizowany wyjątek e1
.
Co dokładnie się tutaj dzieje? Po pierwsze, potrzebujemy nowych wartości wyjątku (różnych lub takich samych). Można do nich zadzwonić nothing
lub null
czy cokolwiek, ale istota pozostaje ta sama - powinny one być nowe wartości, na przykład, że nie powinno być number
w naszym przykładzie tutaj. Wolę nie dzwonić do nich, null
aby uniknąć pomyłek z tym, jak null
można je zaimplementować w dowolnym języku. Tak nothing
samo wolę unikać, ponieważ często się z tym kojarzy null
, co w zasadzie jest tym, co null
należy zrobić, jednak zasada ta często się nagina z jakichkolwiek praktycznych powodów.
Czym dokładnie jest wyjątek?
Jest to trywialna sprawa dla każdego doświadczonego programisty, ale chciałbym dodać kilka słów, aby zgasić robaka zamieszania:
Wyjątek to obiekt kapsułkujący informacje o tym, jak wystąpił nieprawidłowy wynik wykonania.
Może to polegać na wyrzuceniu jakichkolwiek szczegółów i zwróceniu pojedynczej wartości globalnej (jak NaN
lub null
) lub wygenerowaniu długiej listy dziennika lub tego, co się dokładnie wydarzyło, wysłaniu jej do bazy danych i replikacji w całej rozproszonej warstwie przechowywania danych;)
Ważną różnicą między tymi dwoma skrajnymi przykładami wyjątku jest to, że w pierwszym przypadku nie występują żadne skutki uboczne . W drugim są. Co prowadzi nas do pytania (w tysiącach dolarów):
Czy dozwolone są wyjątki w czystych funkcjach?
Krótsza odpowiedź : tak, ale tylko wtedy, gdy nie prowadzą do skutków ubocznych.
Dłuższa odpowiedź. Aby być czystym, wynik funkcji musi być jednoznacznie określony przez jej dane wejściowe. Dlatego zmieniamy naszą funkcję f
, wysyłając 0
do nowej wartości abstrakcyjnej e
, którą nazywamy wyjątkiem. Zapewniamy, że wartość e
nie zawiera żadnych informacji zewnętrznych, które nie są jednoznacznie określone przez nasze dane wejściowe, czyli x
. Oto przykład wyjątku bez efektu ubocznego:
e = {
type: error,
message: 'I got error trying to divide 1 by 0'
}
A oto jeden z efektem ubocznym:
e = {
type: error,
message: 'Our committee to decide what is 1/0 is currently away'
}
W rzeczywistości ma skutki uboczne tylko wtedy, gdy ta wiadomość może się zmienić w przyszłości. Ale jeśli gwarantuje się, że nigdy się nie zmieni, wartość ta staje się wyjątkowo przewidywalna, a zatem nie występuje efekt uboczny.
Aby było jeszcze głupiej. Funkcja powracająca 42
kiedykolwiek jest wyraźnie czysta. Ale jeśli ktoś szalony zdecyduje się 42
na zmienną, której wartość może się zmienić, ta sama funkcja przestaje być czysta w nowych warunkach.
Zauważ, że używam notacji dosłowności obiektowej dla uproszczenia, aby zademonstrować istotę. Niestety rzeczy są pomieszane się w językach takich jak JavaScript, gdzie error
nie jest to typ, który zachowuje się tak jak chcemy tutaj w odniesieniu do kompozycji funkcyjnego, natomiast rzeczywistych typów podobnych null
lub NaN
nie zachowują się w ten sposób, ale raczej przejść przez jakiś sztuczny i nie zawsze intuicyjny typ konwersji.
Rozszerzenie typu
Ponieważ chcemy zmienić komunikat w naszym wyjątku, naprawdę ogłaszamy nowy typ E
dla całego obiektu wyjątku, a następnie to właśnie maybe number
robi, oprócz mylącej nazwy, która ma być albo typem, number
albo nowym typem wyjątku E
, więc jest naprawdę związek number | E
z number
i E
. W szczególności zależy to od tego, jak chcemy zbudować E
, co nie jest ani sugerowane, ani odzwierciedlone w nazwie maybe number
.
Co to jest skład funkcjonalny?
Jest matematyczne funkcje Wykonywanie operacji
f: X -> Y
i g: Y -> Z
i budowaniu ich skład jako funkcję h: X -> Z
zaspokajania h(x) = g(f(x))
. Problem z tą definicją występuje, gdy wynik f(x)
nie jest dozwolony jako argument g
.
W matematyce tych funkcji nie da się ułożyć bez dodatkowej pracy. Rozwiązanie ściśle matematyczne dla powyższego przykładu f
i g
ma zostać usunięte 0
ze zbioru definicji f
. Dzięki temu nowemu zestawowi definicji (nowemu, bardziej restrykcyjnemu typowi x
) f
staje się on kompatybilny z g
.
Ograniczenie zestawu takich definicji nie jest jednak zbyt praktyczne w programowaniu f
. Zamiast tego można zastosować wyjątki.
Lub innym podejściu wartości tworzone są sztuczne jak NaN
, undefined
, null
, Infinity
itd. Więc ocenia 1/0
się Infinity
i 1/-0
do -Infinity
. A następnie wymuś nową wartość z powrotem do wyrażenia zamiast zgłaszania wyjątku. Prowadząc do wyników, które możesz, ale nie musisz, przewidzieć:
1/0 // => Infinity
parseInt(Infinity) // => NaN
NaN < 0 // => false
false + 1 // => 1
Wróciliśmy do regularnych liczb gotowych do przejścia;)
JavaScript pozwala nam nadal wykonywać wyrażenia liczbowe za wszelką cenę bez zgłaszania błędów, jak w powyższym przykładzie. Oznacza to, że pozwala także komponować funkcje. Na tym właśnie polega monada - regułą jest komponowanie funkcji spełniających aksjomaty zdefiniowane na początku tej odpowiedzi.
Ale czy zasada komponowania funkcji wynikająca z implementacji JavaScript do obsługi błędów numerycznych to monada?
Aby odpowiedzieć na to pytanie, wystarczy sprawdzić aksjomaty (pozostawione jako ćwiczenie jako część pytania tutaj;).
Czy można zastosować wyjątek rzucania do skonstruowania monady?
Rzeczywiście, bardziej użyteczną monadą byłaby zamiast tego reguła, która mówi, że jeśli f
zgłasza wyjątek dla niektórych x
, to i jego skład z każdym g
. Plus sprawiają, że wyjątek ten jest E
unikalny na całym świecie i ma tylko jedną możliwą wartość ( obiekt końcowy w teorii kategorii). Teraz dwa aksjomaty są natychmiast sprawdzalne i otrzymujemy bardzo przydatną monadę. Rezultatem jest tak zwana być może monada .