Otwórz menu główne

Monada (programowanie)

Monada – w programowaniu funkcyjnym: rodzaj konstruktora abstrakcyjnego typu danych, który implementuje funkcje wiązania (ang. bind) oraz jednostki (ang. unit)[1][2]. Jednostka określa operację tworzenia singletonu czy też akcji, której wynikiem jest zadana wartość. Wiązanie (w niektórych językach określane jako flat map) buduje na podstawie wyniku poprzedniego obliczenia nowe obliczenie, zachowując przy tym jego formę.

Jedną z głównych cech monad jest to, że pozwalają wynieść pewne mechanizmy i zachowania do abstrakcji zwalniając tym samym programistę z konieczności ręcznej obsługi kontekstu. Przykładowo, działając na (jakkolwiek zdefiniowanych) zbiorach można opisać jednostkę jako

oraz wiązanie jako

by potem w sposób generyczny budować potoki danych w kompletnym oderwaniu od wewnętrznej implementacji czy nawet interpretacji. Dla przykładu wymnażanie elementów dwóch zbiorów mogłoby przebiegać tak:

Istotne w tym przykładzie to, że przy definiowaniu operacji mul ani razu nie skorzystano z faktu, że nośnikiem danych jest matematyczny zbiór. Będzie ona zatem działać dla każdej monady, bez względu na jej wewnętrzną reprezentację.

To podejście stanowi popularny wzorzec projektowy, który ma szerokie zastosowanie przy definiowaniu obliczeń zawierających dodatkowe reguły przetwarzające, jak chociażby transformowanie stanu, współbieżność, obsługę wejścia-wyjścia lub wyjątków).

PrzykładyEdytuj

HaskellEdytuj

W Haskellu monada jest klasą zrzeszającą typy o rodzaju  

class Monad m where
  (>>=) :: m a -> (a -> m b) -> m b
  (>>) :: m a -> m b -> m b
  return :: a -> m a
  fail :: String -> m a

Operacje wiązania >> oraz >>=, łączą ze sobą dwie wartości monadyczne, podczas gdy operacja return umieszcza wartość w monadzie (kontenerze). Działanie operacji >>= może być wyjaśnione przy pomocy jej sygnatury: ma >>= \v -> mb polega na połączeniu wartości monadycznej ma, zawierającej wartość typu a z funkcją, operującą na wartości v typu a, zwracającej wartość monadyczną mb. Rezultatem jest więc połączenie wartości ma oraz mb w wartość monadyczną, zawierającą b. Operacja >> jest używana wtedy, gdy stosowana funkcja nie wymaga wartości ma. Haskell posiada syntaktyczne wsparcie monad przy użyciu tzw. notacji do:

bez_do :: (Monad m, Alternative m) => m Int -> m Int -> m Int
bez_do m1 m2 =
  m1 >>= \x1 -> m2 >>= \x2 -> guard (x2 /= 0) >> return (x1 / x2)
  
z_do :: (Monad m, Alternative m) => m Int -> m Int -> m Int
z_do m1 m2 = do
  x1 <- m1
  x2 <- m2
  guard (x2 /= 0)
  return (x1 / x2)


ScalaEdytuj

W Scali monadą jest każda klasa, która implementuje operacje flatMap oraz unit

trait M[A] {
  def flatMap[B](f: A => M[B]): M[B]
}
  
def unit[A](x: A): M[A]

Odpowiednikiem notacji do w Scali jest for comprehensions:

def bez_for(m1 : M[Int], m2 : M[Int]) : M[Int] =
  m1.flatMap(x1 => m2.flatMap(x2 => unit[M](x1 + x2)))
  
def z_for(m1 : M[Int], m2 : M[Int]) : M[Int] =
  for {
    x1 <- m1
    x2 <- m2
  } yield (x1 + x2)

PrzypisyEdytuj

  1. kell Programming From First Principles Christopher Allen, Julie Moronuki. Strony 755-799
  2. Demystifying the Monad in Scala, Developer News, 4 grudnia 2015 [dostęp 2019-07-20] (ang.).