Монади-2

| Прокоментувати

(Disclaimer: текст пишеться в першу чергу для того, щоби самому якнайповніше зрозуміти концепцію. Тому якщо для когось надто просто та банально або навпаки надто складно - вибачайте :-)

З прикладної точки зору, монади для функціонального програміста - такий самий design pattern, як для об'єктно-орієнтованого програміста адаптер чи абстрактна фабрика. Вони абсолютно необхідні для розв'язання реальних задач в тих функціональних мовах, які не підтримують поняття змінних та стану обчислень. OCaml в цьому розумінні не такий категоричний: mutable-поля в структурах є. Але чистий функціональний підхід має свої переваги, про які іншим разом.

Згідно з визначенням Філіпа Вадлера (він бородатий! :-) монада це:

Оператор M над типами разом з трійкою функцій


  • map

  • unit

  • join

Оператор над типами (в літературі - функтор) - це абстрактна функція , яка перетворює один тип в інший.

Мушу сказати, що цей момент мене трохи заплутував, коли я тільки перейшов з C++ на OCaml: в літературі по С++ функтором часто називають об'єкт будь-якого класу, для якого визначений operator(), в той час, коли насправді функтор в C++ це те, що виділено жирним в наступних рядках:

std::vector<std::string> vs;
std::list<std::pair<int, string> > lp;

В першому прикладі функтор створює тип вектор рядків з типу рядок. В другому - два функтори вкладені одне в одного: внутрішній функтор з двох простих типів створює тип пара, а зовнішній - з цього типу пара створює тип список пар.

Функція map перетворює значення одного "монадного" типу в значення іншого "монадного" типу. Наприклад, нехай в нас в якості оператора M виступає той самий std::vector. Також в нас є функція, яка перетворює значення цілого в пару: 1 -> (1,"один"), 2 -> (2,"два"), і т.д. Тоді map, прийнявши в якості параметра таку функцію, зможе перетворити для нас вектор цілих у вектор пар; значення одного "монадного" типу у значення іншого. Як на мене - круто :-)

В реальному світі монадою часто називають конкретне значення "монадного" типу, що було отримане в результаті застосування функції unit до значення деякого вихідного, "немонадного", типу. На OCaml це може виглядати так:

# let unit x = [x] ;;
val unit : 'a -> 'a list = <fun>
# let a = unit 5 ;;
val a : int list = [5]
# let b = 1 :: unit 10 ;;
val b : int list = [1; 10]
# let c = "a" :: unit "test" ;;
val c : string list = ["a"; "test"]

Така функція візьме на вхід будь-що і зробить з цього "будь-що" список з одного елемента.

Функція join потрібна для компенсації ефектів, що виникають при композиції кваліфікаторів, в результаті чого вихідний тип x виявляється "загорнутим" в монаду двічі.

Продовження, безумовно, буде...

Сторінки

Про цей запис

This page contains a single entry by Микола Стрєбков published on November 20, 2009 12:55 AM.

Надто багато новин про Росію was the previous entry in this blog.

Музика is the next entry in this blog.

Свіжі записи можна подивитись на головній сторінці. Все інше - в архіві.