Мона́да — особый тип данных в функциональных языках программирования, для которого возможно задать императивную последовательность выполнения некоторых операций над хранимыми значениями. Монады позволяют задавать последовательность выполнения операций, производить операции с побочными эффектами и другие действия, которые сложно или вовсе невозможно реализовать в функциональной парадигме программирования другими способами.

Концепция монады и термин изначально происходят из теории категорий, где она определяется как функтор с дополнительной структурой. Исследования, начатые в конце 1980-х — начале 1990-х годов, установили, что монады могут привнести, казалось бы, разрозненные проблемы компьютерной науки в единую функциональную модель. Теория категорий также выдвигает несколько формальных требований, так называемых монадических законов, которые должны соблюдаться любой монадой и могут быть использованы для верификации монадического кода.

Описание Монады чаще всего используются в функциональных языках программирования. При ленивой модели вычисления порядок редукции неизвестен. Например, вычисление 1 + 3 + 6 может быть редуцировано в 1 + 9 или 4 + 6. Монады позволяют упорядочить редукцию. Поэтому существует ироничное утверждение, что монады — это способ перегрузить оператор «точка с запятой».

Монада является контейнером, который хранит в себе значение произвольного типа. Она должна обладать функцией связывания (bind), которая принимает два аргумента: текущее значение монады и функцию, принимающую значение типа, который содержит текущая монада и возвращающая новую монаду. Результатом вызова функции связывания будет новая монада, полученная путём применения первого аргумента ко второму. Так могла бы выглядеть монада в императивном языке Java и одна из её реализаций, контейнер Maybe: import java.util.function.Function;

interface Monad<T> { <U> Monad<U> bind(Function<T, Monad<U>> f); }

class Maybe<T> implements Monad<T> {

private final T val;

public Maybe(T val) { this.val = val;

public T getVal() { return val;

@Override public <U> Monad<U> bind(Function<T, Monad<U>> f) { if (val == null) return new Maybe<U>(null); return f.apply(val);

public class MonadApp { public static void main(String[] args) { Maybe<Integer> x = new Maybe<>(5); Monad<Integer> y = x .bind(v -> new Maybe<>(v + 1)) .bind(v -> new Maybe<>(v * 2)); System.out.println( ((Maybe<Integer>)y).getVal() );

Появившиеся в Java 8 функциональные интерфейсы позволяют реализовать интерфейс, похожий на монаду.

В Haskell Класс Monad присутствует в стандартном модуле Prelude. Для реализации данного класса требуется любой однопараметрический тип (тип рода * -> *). Монада обладает тремя методами

class Functor f where fmap :: (a -> b) -> f a -> f b

class Functor f => Applicative f where pure :: a -> f a (<*>) :: f (a -> b) -> f a -> f b (*>) :: f a -> f b -> f b (<*) :: f a -> f b -> f a

-- m :: * -> * class Applicative m => Monad m where (>>=) :: m a -> (a -> m b) -> m b (>>) :: m a -> m b -> m b -- реализован по-умолчанию: a >> b = a >>= \_ -> b return :: a -> m a -- = pure

Метод return может ввести в заблуждение программистов, знакомых с императивными языками: он не прерывает вычисления, а лишь упаковывает произвольное значение типа a в монаду m. Оператор >>= является функцией связывания. Оператор >> — частный случай оператора >>=, используется когда нам не важен результат связывания.

До версии GHC 8.0 в классе Monad присутствовал метод fail, который не имел отношения к теоретической сущности монад, однако использовался в случае ошибки сопоставления с образцом внутри монадического вычисления.Монады — «обобщение некоторых привычных идиом, а также как ещё один метод для их абстракции».). Этот метод был перенесён в класс MonadFail.

Некоторые типы, реализующие класс Monad: IO, используется для функций с побочным эффектом. Конструкторы IO скрыты от программиста, также отсутствуют функции распаковки монады. Это не позволяет вызывать грязные функции из чистых. Maybe. Вычисление прерывается, если получено значение Nothing. [] (список). Вычисление прерывается при пустом списке. При непустом списке оператор >>= вызывает функцию для каждого элемента списка. Reader. Writer. State. Помимо возможностей Reader позволяет изменять состояние.

В языке также присутствует do-нотация, которая является более удобной формой записи монадических функций. В данном примере f1 использует do-нотацию, а f2 записана с помощью операторов связывания: f1 = do s <- getLine putStrLn $ "Hello " ++ s putStrLn "Goodbye"

f2 = getLine >>= (\s -> putStrLn $ "Hello " ++ s) >> putStrLn "Goodbye"

Примечания

Ссылки

Учебные пособия Monad Tutorials Timeline Большая коллекция пособий по монадам, представлены в порядке появления. What the hell are Monads? You Could Have Invented Monads! (And Maybe You Already Have.), простое введение Monads as Computation Monads as Containers Monads for the Working Haskell Programmer The Haskell Programmer’s Guide to the IO Monad — Don’t Panic Introduction to Haskell, Part 3: Monads On Monads Crash Monad Tutorial — статья о монадах, объясняющая их с точки зрения теории категорий Learn You a Haskell for Great Good!  — книга содержит доступное описание языка Haskell, в котором много внимания уделено понятию монады и аналогичным конструкциям

Другие статьи A tour of the Haskell Monad functions Notions of Computation and Monads от Eugenio Moggi, первая статья, предлагающая использование монад в программировании «Monads for Functional Programming» от Philip Wadler, описание монад в языке Хаскелл (написано ещё до того, как они в нём появились) 4. Монады — простое изложение основ языка

Литература

Категория:Теория категорий Категория:Haskell Категория:Концепции языков программирования