Pochodzę z tła C #, gdzie LINQ ewoluował w Rx.NET, ale zawsze interesował się FP. Po krótkim wprowadzeniu do monad i niektórych pobocznych projektach na F # byłem gotów spróbować przejść do następnego poziomu.
Teraz, po kilku rozmowach na temat wolnej monady od ludzi ze Scali i licznych napisaniach w Haskell lub F #, znalazłem gramatyki z tłumaczami dla zrozumienia, które są dość podobne do IObservable
łańcuchów.
W FRP tworzysz definicję operacji z mniejszych fragmentów specyficznych dla domeny, w tym skutków ubocznych i awarii, które pozostają w łańcuchu, i modelujesz swoją aplikację jako zestaw operacji i efektów ubocznych. W wolnej monadzie, jeśli dobrze to zrozumiałem, robisz to samo, wykonując operacje jako funktory i podnosząc je za pomocą coyoneda.
Jakie byłyby różnice między tymi, które przechylają igłę w kierunku któregokolwiek z podejść? Jaka jest podstawowa różnica przy definiowaniu usługi lub programu?
źródło
Cont
jest to jedyna monada, jaką widziałem, której nie można wyrazić za pomocą wolnej monady, prawdopodobnie można założyć, że FRP może być. Jak prawie wszystko inne .IObservable
jest instancją monady kontynuacyjnej.Odpowiedzi:
Monady
Monada składa się z
Endofunctor . W naszym świecie inżynierii oprogramowania możemy powiedzieć, że odpowiada to typowi danych z pojedynczym, nieograniczonym parametrem typu. W języku C # byłoby to coś w rodzaju:
Dwie operacje zdefiniowane dla tego typu danych:
return
/pure
przyjmuje „czystą” wartość (tj.T
wartość) i „zawija” ją w monadę (tzn. tworzyM<T>
wartość). Ponieważreturn
jest to zastrzeżone słowo kluczowe w języku C #,pure
odtąd będę się odnosił do tej operacji. W języku C #pure
byłaby metoda z podpisem:bind
/flatmap
przyjmuje wartość monadyczną (M<A>
) i funkcjęf
.f
przyjmuje wartość czystą i zwraca wartość monadyczną (M<B>
). Z nichbind
tworzy nową wartość monadyczną (M<B>
).bind
ma następujący podpis w języku C #:Ponadto, aby być monadą
pure
ibind
muszą przestrzegać trzech praw monady.Teraz jednym ze sposobów modelowania monad w języku C # byłoby zbudowanie interfejsu:
(Uwaga: aby wszystko było krótkie i wyraziste, w ramach tej odpowiedzi skorzystam z pewnej wolności z kodem).
Teraz możemy zaimplementować monady dla konkretnych typów danych, wdrażając konkretne implementacje
Monad<M>
. Na przykład możemy zaimplementować następującą monadę dlaIEnumerable
:(Celowo używam składni LINQ do wywołania związku między składnią LINQ a monadami. Pamiętaj jednak, że możemy zastąpić zapytanie LINQ wywołaniem do
SelectMany
).Czy możemy teraz zdefiniować monadę
IObservable
? Wydawałoby się tak:Aby mieć pewność, że mamy monadę, musimy udowodnić jej prawa. Może to być trywialne (i nie znam się na Rx.NET, aby wiedzieć, czy można je udowodnić na podstawie samej specyfikacji), ale jest to obiecujący początek. Aby ułatwić pozostałą część tej dyskusji, załóżmy, że obowiązują w tym przypadku prawa monad.
Darmowe Monady
Nie ma pojedynczej „wolnej monady”. Wolne monady są raczej klasą monad zbudowanych z funktorów. To znaczy, biorąc pod uwagę funktor
F
, możemy automatycznie wyprowadzić monadęF
(tj. Wolną monadęF
).Functors
Podobnie jak monady, funktory można zdefiniować za pomocą następujących trzech elementów:
Dwie operacje:
pure
otacza czystą wartość funktorem. Jest to analogiczne dopure
monady. W rzeczywistości dla funktorów, które są również monadami, oba powinny być identyczne.fmap
odwzorowuje wartości na wejściu na nowe wartości na wyjściu za pomocą danej funkcji. Jego podpis to:Podobnie jak monady, funktory są zobowiązane do przestrzegania praw funktorów.
Podobnie jak monady, możemy modelować funktory za pomocą następującego interfejsu:
Teraz, ponieważ monady są podklasą funktorów, moglibyśmy również
Monad
trochę zmienić :Tutaj dodałem dodatkową metodę
join
i zapewniłem domyślne implementacje zarównojoin
ibind
. Należy jednak pamiętać, że są to definicje kołowe. Więc musisz zastąpić co najmniej jedno lub drugie. Zauważ też, żepure
teraz jest dziedziczonyFunctor
.IObservable
i darmowe monadyPonieważ zdefiniowaliśmy monadę dla,
IObservable
a ponieważ monady są podklasą funktorów, z tego wynika, że musimy być w stanie zdefiniować instancję funktoraIObservable
. Oto jedna definicja:Teraz, gdy mamy zdefiniowany funktor
IObservable
, możemy z niego zbudować wolną monadę. I właśnie takIObservable
odnosi się do wolnych monad - mianowicie, że możemy zbudować wolną monadęIObservable
.źródło