Co to jest monada w Scali?

Maria Kucharczyk

10 May 2022.3 minutes read

Co to jest monada w Scali? webp image

W matematyce monada to monoid w kategorii endofunktorów.

Brzmi dość abstrakcyjnie jeśli nie jesteś matematykiem, prawda? Być może jesteś już po lekturze 3 artykułów o działaniu monad, analizie kilku prezentacji wokół ich zastosowania oraz obejrzeniu zarówno krótkich jak i długich tutoriali na YouTube i… masz pojęcie czym są monady, jednak nadal zastanawiasz się nad konkretnym przykładem ich realizacji w kodzie.

Jeśli utożsamiasz się z tym opisem, czytaj dalej. Dziś przedstawię ci metaforę, dzięki której może lepiej wyobrazisz sobie monady. A kto wie, może nawet zapragniesz programować funkcyjnie? 😁 Jedno jest pewne - poznasz prosty przepis na to, jak stworzyć monadę!

Co to jest monada?

Monada to pojęcie matematyczne zaciągnięte wprost z matematycznej teorii kategorii. Języki funkcyjne, takie jak Scala czy Haskell, często korzystają z matematycznych abstrakcji, żeby lepiej modelować problemy, które za ich pomocą rozwiązujemy. Dlatego na przykładzie takich języków najlepiej będzie wytłumaczyć monadę.

Tak w prosty sposób wyjaśnia monadę Jacek Kunicki, nasz Senior Scala Engineer:

W programowaniu funkcyjnym monada to mechanizm umożliwiający wykonywanie obliczeń (reprezentowanych przez funkcje) w sposób sekwencyjny.

Podobnym na pierwszy rzut oka mechanizmem jest funktor (umożliwiający wykonywanie operacji map), jednak pomiędzy monadą a funktorem jest zasadnicza różnica - za chwilę dowiesz się jaka.

Monadę można również traktować jako opakowanie na wartości innych typów. W języku programowania Scala monada F będąca opakowaniem na jakiś typ A jest typu F[A].

Na monadę składa się:

  • funkcja A => F[A], nazywana pure lub return (rzadziej unit), pozwalająca opakować dowolną wartość typu A w monadę,
  • funkcja F[A] => (A => F[B]) => F[B], nazywana flatMap (m. in. w Scali) lub bind (np. w Haskellu), która umożliwia składanie funkcji zwracających monadę w ciąg sekwencyjnych wywołań.

Definicja interfejsu monady w Scali wyglądałaby następująco:

trait Monad[F[_]]:
  def pure[T](t: T): F[T]
  def flatMap[T, U](a: F[T])(f: T => F[U]): F[U]

Wróćmy teraz do porównania z funktorem.

Funkcja map w funktorze F ma sygnaturę: F[A] => (A => B) => F[B]. Zauważ, że różnica względem flatMap występuje w funkcji “wewnętrznej”, zamieniającej wartości typu A na wartości typu B. W przypadku map funkcja ta zwraca wartość typu B, a więc bez opakowania. W przypadku flatMap typem zwracanym jest F[B], a więc wartość typu B opakowana w monadę F.

Załóżmy, że mamy pewną funkcję f: A => F[B]. Gdybyśmy wywołali funkcję F[A].map(f), to w wyniku otrzymalibyśmy wartość typu F[F[B]], a więc zagnieżdżone opakowanie. Z kolei wywołując F[A].flatMap(f), otrzymamy wartość typu F[B], czyli bez zagnieżdżonego opakowania - mimo że zaczynaliśmy od wartości w opakowaniu i użyliśmy funkcji zwracającej opakowaną wartość.

To właśnie dzięki takiemu zachowaniu monady umożliwiają składanie wielu funkcji zwracających tę samą monadę w ciąg sekwencyjnych wywołań. Metoda flatMap odpowiada za wywołanie funkcji zmieniającej wartość oraz za “spłaszczanie” zagnieżdżonych opakowań pojawiających się w toku kolejnych obliczeń.

Jeśli chcesz wdrożyć się jeszcze konkretniej w temat monad w Scali, blog “Monoid in the Category of Endofunctors” na pewno Cię zainteresuje.

Polecam też krótki tutorial naszego CTO, który rozwija temat zastosowania Monad w Scali.

A tymczasem, by wyobrazić sobie monadę jeszcze lepiej, idźmy o krok dalej...

Czym właściwie jest monada? Nie tylko w programowaniu!

W filozofii monada to najbardziej podstawowa lub oryginalna substancja. To jak placek do kebaba dla programisty(-ki). Przyznaj się - kiedy jadłeś(-aś) go ostatnio?

W placek (monadę) zawijamy (opakowujemy) wkład wejściowy: mięso lub/i warzywa (wartości dowolnego typu), otrzymując w ten sposób pysznego kebaba.

Już dziś możesz spróbować przepisu na Placek de la Monada, programując w domu.

Placek de la Monada

  1. Do miski wsyp 2,5 szklanki mąki i szczyptę soli.
  2. Dodaj 5 łyżek oliwy i niepełną szklankę gorącej wody.
  3. Wygniataj.
  4. Przykryj ścierką, odstaw na pół godziny.
  5. Podziel ciasto na 5-6 części.
  6. Wałkuj na cienkie placki.
  7. Rozgrzej dobrze patelnię.
  8. Smaż placki na suchej patelni przez około minutę z każdej strony.
  9. Zbuduj stos z usmażonych placków, przykryj stos pokrywką i poczekaj aż ostygną.
  10. Pobieraj placki de la Monada ze stosu w kolejności LIFO.

Co dalej? Więcej mięsa (dosłownie!) znajdziesz w Functional Programming Cookbook! Kliknij w poniższy obrazek, by przejść na stronę, gdzie możesz go pobrać.
FP-cookbook-monad 

Blog Comments powered by Disqus.