Co to jest monada w programowaniu funkcyjnym?

Co to jest monada w programowaniu funkcyjnym?
UB
  • Rejestracja:około 17 lat
  • Ostatnio:ponad 7 lat
0

Co to jest monada? Czy jest to po prostu kontener?
Tzn zdaje sobie sprawę z dwóch obowiązkowych funkcji (return, bind czyli apply i flatMap) ale jest np takie zdanie na wikipedii:

Programy pisane w stylu funkcyjnym mogą korzystać z monad do strukturalizowania procedur zawierających operacje wykonywane sekwencyjnie, albo definiować pewne wymagane przepływy sterowania (takie jak współbieżność, kontynuacje, efekty uboczne tj. obsługa I/O lub wyjątki).

Jak to rozumieć?

edytowany 2x, ostatnio: ubuntuser
n0name_l
Hmm... szczerze watpie zeby ktos Ci tu na forum to opisal w paru zdaniach, Jak juz to celowalbym w MSM'a. + pare linkow: http://stackoverflow.com/questions/44965/what-is-a-monad i http://ericlippert.com/category/monads/ + ew. w Programming in Scala bylo wyjasnienie na przykladach w scali, ale tez raczej takie 'plytkie'.
UB
dzięki za linki, coś mi w głowie otworzyły.
OA
  • Rejestracja:ponad 13 lat
  • Ostatnio:prawie 10 lat
  • Postów:95
5

Cytując klasyka: A monad is just a monoid in the category of endofunctors. (źródło)

Monady mogą być użyte do symulacji IO w językach funkcyjnych, ale wcale tak być nie musi. Monada to coś dużo bardziej ogólnego i wcale nie związanego jakoś specjalnie z IO. Monadę tworzą dwie operacje:

  1. unit :: a -> m a - opakowanie "czystej" wartości w swoiste monadyczne pudełko.
  2. bind :: m a -> (a -> m b) -> m b - otwieramy monadyczne pudełko, manipulujemy wartością, zamykamy pudełko (otrzymując nową wartość monadyczną).
    Które spełniają prawa monad
  3. unit(a).bind(f) == f(a)
  4. m.bind(unit) == m
  5. m.bind(f).bind(g) == m.bind(λx -> f(x).bind(g))
    W praktyce prawa te zapewniają intuicyjne zachowanie. Dużo łatwiej zobaczyć dlaczego powinny być spełnione w haskellowej notacji do, która po kompilacji korzysta z return (unit) oraz >>= (bind).
Kopiuj
do { y <- return x; f y } == do { f x } 
Kopiuj
do { x <- m; return x } == do { m } 
Kopiuj
do { y <- do { x <- m
               f x
             }
     g y
   }
==
do { x <- m
     do { y <- f x
          g y
        }
   }
==
do { x <- m
     y <- f x
     g y
   }

Z praktycznego punktu widzenia monady pozwalają na wygodne odseparowanie sposobu łączenia wyników w jedno od samych operacji, które chcemy przeprowadzić.

Przykład: monada Maybe; reprezentuje obliczenia, które mogą się nie powieść. Jest przydatna do kodu w rodzaju:

Kopiuj
var x = findSomething(); // Może zwrócić null, jeśli nic nie znajdzie!
if (x === null) return null;
var y = findSomethingElse(x); // Znów może zwrócić null, gdy nie znajdzie.
if (y === null) return null;
var z = lookup(x,y); // Same story...
if (z === null) return null;
return z;

Taki kod jest nieelegancki. Cały czas musimy się powtarzać. Chcemy po prostu napisać:

Kopiuj
var x = findSomething();
var y = findSomethingElse(x);
return lookup(x,y);

bez martwienia się o ciągłe sprawdzanie czy coś nie jest przypadkiem null-em. Monadycznie można by to zapisać jako:

Kopiuj
var Maybe = function(x) {
    return {
        bind: function(f) {
            return (x === null) ? null : f(x);
        }
    };
};

var findSomething = function() { return Maybe(3); };
var someFailure = function() { return Maybe(null); };
var findSomethingElse = function(x) { return Maybe(x+5); };
var lookup = function(x,y) { return Maybe(x*y); };

findSomething().bind(function(x) {
    return findSomethingElse(x).bind(function(y) {
        return someFailure().bind(function() {
            return lookup(x,y).bind(function(z) {
                print('To sie nie wykona');
            });
        });
    });
});

findSomething().bind(function(x) {
    return findSomethingElse(x).bind(function(y) {
        return lookup(x,y).bind(function(z) {
            print('val = ' + z);
        });
    });
});

(ideone)
Oczywiście wygląda to okropnie, ale już np. w Haskellu wygląda dużo lepiej, dzięki notacji do, która pod spodem robi mniej więcej to samo co ten kod Javascript.

Kopiuj
import Prelude hiding(Maybe(..), lookup)


data Maybe a = Just a | Nothing deriving (Show)
instance Monad Maybe where
  return x = Just x
  (Just x) >>= f = f x
  Nothing >>= _  = Nothing

findSomething :: Maybe Int
findSomething = return 3

findSomethingElse :: Int -> Maybe Int
findSomethingElse x = return (x+5)

lookup :: Int -> Int -> Maybe Int
lookup x y = return (x*y)

someFailure :: Maybe a
someFailure = Nothing

findThemAll :: Maybe Int
findThemAll = do
  x <- findSomething
  y <- findSomethingElse x
  lookup x y

findThemAllFail :: Maybe Int
findThemAllFail = do
  x <- findSomething
  someFailure
  y <- findSomethingElse x
  lookup x y
  
main = print findThemAll >> print findThemAllFail

(ideone)

edytowany 3x, ostatnio: Oak
UB
  • Rejestracja:około 17 lat
  • Ostatnio:ponad 7 lat
0

Chyba pojąłem o co chodzi z monadami jako łączenie (łańcuchowanie - chaining) operacji na "stanie świata" i idee braku efektów ubocznych. W takim razie, jak mają się do tego kolekcje w Scali?

Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:22 minuty
2

Każda kolekcja, w tym Option, jest monadą. Ale monadami są jeszcze inne rzeczy, np Either.
Monady można by zaklepać i w Javie, tylko kod byłby paskudny bez Haskellowego 'do' czy Scalowego 'spimpowanego for'.

Oprócz kolekcji jest jeszcze wiele innych monad. Dla przykładu kontynuacje: Kontynuacje

Opisanie czym jest monada na chłopski rozum jest tak trudne, że chyba nikt tego tak wprost nie wytłumaczył :P Dlatego szukanie takiego prostego opisu jest raczej antyproduktywne. Moim zdaniem lepiej po prostu porozpisywać sobie jak działają monady, tzn wziąć coś z Haskella w notacji 'do' albo w Scali z użyciem 'for' i rozpisać to na wywołania funkcji monadycznych.

Oak powyżej dał bardzo ładny przykład jak monada Maybe w Haskellu (ale tak samo byłoby z szeroko używaną monadą Option w Scali) ładnie upraszcza kod w przypadku gdy chcemy pozwolić by funkcje nic nie zwracały. W Javie od tego jest null oraz drabinki ifów sprawdzających czy coś jest nullem, co jest nieczytelne i rozwlekłe - dlatego się tak nie robi. Czasami się tylko dodaje adnotację @Nullable lub @NotNullable, ale to nie rozwiązuje tylu problemów co Maybe/ Option (ale przy okazji nie dodaje narzutu, więc jest coś za coś).


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)