Щоб поважати швидких читачів, я спочатку починаю з точного визначення, продовжую з швидкого більш "простого англійського" пояснення, а потім переходжу до прикладів.
Ось і стисле, і чітке визначення, дещо перероблене:
Монада (в інформатиці) формально карта , яка:
посилає кожен тип X
деякої заданої мови програмування до нового типу T(X)
(називається "тип T
-вичислень зі значеннями в X
");
оснащений правилом складання двох функцій форми
f:X->T(Y)
та g:Y->T(Z)
функції g∘f:X->T(Z)
;
таким чином, який є асоціативним у явному сенсі та неінітальним щодо заданої одиничної функції, яка називається pure_X:X->T(X)
, вважати таким, що приймає значення до чистого обчислення, яке просто повертає це значення.
Отже, простими словами, монада - це правило переходу від будь-якого типу X
до іншого типуT(X)
, а правило - перехід від двох функцій f:X->T(Y)
і g:Y->T(Z)
(що ви хотіли б скласти, але не можете) до нової функціїh:X->T(Z)
. Що, однак, не є композицією в суворому математичному сенсі. Ми в основному "згинаємо" склад функції або переосмислюємо, як складаються функції.
Крім того, нам потрібно правило складання монади, щоб задовольнити "очевидні" математичні аксіоми:
- Асоціативність : Композиція
f
з, g
а потім із h
(ззовні) повинна бути такою ж, як композиція g
з, h
а потім із f
(зсередини).
- Унітальна властивість : Композиція
f
з функцією тотожності з будь-якої сторони має принести результат f
.
Знову кажучи, простими словами, ми не можемо просто з глузду переосмислити склад своєї функції, як нам подобається:
- Спочатку нам потрібна асоціативність, щоб мати можливість складати кілька функцій підряд, наприклад
f(g(h(k(x)))
, і не турбуватися про те, щоб вказати пари функцій, що складають функції. Оскільки правило монади лише прописує, як скласти пару функцій , без цієї аксіоми нам слід було б знати, яка пара складається першою тощо. (Зверніть увагу , що відрізняється від властивості коммутативности , що в f
складі з g
були такими ж , як у g
складі з f
, що не потрібно).
- По-друге, нам потрібна унітальна властивість, яка просто говорить про те, що особистість складається тривіально так, як ми їх очікуємо. Таким чином, ми можемо безпечно виконувати функції рефактора кожного разу, коли ці особи можна отримати.
Отже, коротко: монада - це правило розширення типу та складання функцій, що відповідають двом аксіомам - асоціативності та унітальній властивості.
На практиці ви хочете, щоб монада була реалізована для вас мовою, компілятором або рамкою, яка б відповідала за складання функцій для вас. Таким чином, ви можете зосередитись на написанні логіки вашої функції, а не турбуватися про те, як здійснюється їх виконання.
Це, по суті, в двох словах.
Будучи професійним математиком, я вважаю за краще уникати називати h
"склад" f
та g
. Тому що математично це не так. Називаючи це "композицією", неправильно припускається, що h
це справжній математичний склад, який він не є. Це навіть не однозначно визначається f
і g
. Натомість це результат нового "правила складання" монади нашої монади. Що може абсолютно відрізнятися від фактичного математичного складу, навіть якщо останній існує!
Щоб зробити його менш сухим, дозвольте спробувати проілюструвати на прикладі, що я коментую невеликі ділянки, тож ви можете пропустити прямо до точки.
Кидання винятків як приклади Монади
Припустимо, ми хочемо скласти дві функції:
f: x -> 1 / x
g: y -> 2 * y
Але f(0)
не визначено, тому e
викидається виняток . Тоді як можна визначити композиційне значення g(f(0))
? Знову киньте виняток, звичайно! Може, те саме e
. Можливо, новий оновлений виняток e1
.
Що саме тут відбувається? По-перше, нам потрібні нові значення (и) виключення (різні або однакові). Ви можете назвати їх nothing
або null
або будь-який інший, але суть залишається та ж - вони повинні бути нові значення, наприклад , це не повинно бути number
в нашому прикладі тут. Я вважаю за краще не називати їх, null
щоб уникнути плутанини з тим, як null
можна реалізувати будь-яку конкретну мову. Так само я вважаю за краще уникати, nothing
тому що це часто асоціюється з тим null
, що, в принципі, є тим, що null
слід робити, однак цей принцип часто збивається з будь-яких практичних причин.
Що саме є винятком?
Це дрібниця для будь-якого досвідченого програміста, але я хотів би залишити кілька слів, щоб усунути будь-якого хробака плутанини:
Виняток - це об'єкт, який містить інформацію про те, як стався недійсний результат виконання.
Це може варіюватися від викидання будь-яких деталей і повернення єдиного глобального значення (наприклад, NaN
або null
) або створення довгого списку журналів або того, що саме сталося, надсилання його в базу даних та реплікації по всьому розподіленому шару зберігання даних;)
Важлива відмінність цих двох крайніх прикладів винятку полягає в тому, що в першому випадку немає побічних ефектів . У другій є. Що підводить нас до (тисячі доларів) питання:
Чи допускаються винятки в чистих функціях?
Коротша відповідь : Так, але лише тоді, коли вони не призводять до побічних ефектів.
Більш довга відповідь. Щоб бути чистим, результат вашої функції повинен бути однозначно визначений її входом. Таким чином, ми вносимо зміни до своєї функції f
, надсилаючи 0
нове абстрактне значення, e
яке ми називаємо винятком. Ми переконуємося, що значення e
не містить зовнішньої інформації, яка не визначається однозначно нашим вкладом, що є x
. Ось ось приклад виключення без побічних ефектів:
e = {
type: error,
message: 'I got error trying to divide 1 by 0'
}
І ось один із побічними ефектами:
e = {
type: error,
message: 'Our committee to decide what is 1/0 is currently away'
}
Насправді це має лише побічні ефекти, якщо це повідомлення може змінитися в майбутньому. Але якщо гарантується, що він ніколи не зміниться, це значення стане однозначно передбачуваним, і тому немає побічних ефектів.
Щоб зробити це ще дурніше. Функція, що повертається 42
колись, явно чиста. Але якщо хтось божевільний вирішить зробити 42
змінну, це значення може змінитися, сама ж функція перестає бути чистою в нових умовах.
Зауважте, що я використовую об'єктні буквальні позначення для простоти, щоб продемонструвати суть. На жаль, речі переплутані такими мовами, як JavaScript, де error
це не тип, який веде себе так, як ми хочемо, стосовно композиції функцій, тоді як фактичні типи подобаються null
чи NaN
не так поводяться, а краще проходять через якісь штучні та не завжди інтуїтивні тип конверсій.
Тип розширення
Оскільки ми хочемо змінити повідомлення всередині нашого винятку, ми дійсно оголошуємо новий тип E
для всього об’єкта винятку, і тоді це maybe number
робиться, окрім його заплутаного імені, яке має бути або типу, number
або нового типу винятку E
, так що це дійсно союз number | E
з number
і E
. Зокрема, це залежить від того, як ми хочемо побудувати E
, що ні запропоновано, ні відображено в назві maybe number
.
Що таке функціональний склад?
Це математичні функції операції , які беруть
f: X -> Y
і g: Y -> Z
та побудова їх складу як функція , h: X -> Z
яка задовольнить h(x) = g(f(x))
. Проблема з цим визначенням виникає, коли результат f(x)
не дозволений як аргумент g
.
У математиці ці функції неможливо скласти без додаткової роботи. Суворо математичне рішення для нашого вище прикладу f
і g
полягає в тому, щоб вилучити 0
з набору визначення f
. З цим новим набором визначення (новий більш обмежувальний тип x
) f
стає компонувати з g
.
Однак в програмуванні не дуже практично обмежувати набір f
подібного визначення . Натомість можна використовувати винятки.
Або як інший підхід, штучні цінності створюються , як NaN
, undefined
, null
, і Infinity
т.д. Таким чином , ви оцінюєте 1/0
до Infinity
і 1/-0
до -Infinity
. А потім примушуйте нове значення повертатися у свій вираз, а не викидати виняток. Приводячи до результатів, ви можете або не можете вважати передбачуваними:
1/0 // => Infinity
parseInt(Infinity) // => NaN
NaN < 0 // => false
false + 1 // => 1
І ми повертаємося до звичайних номерів, готових рухатися далі;)
JavaScript дозволяє нам будь-якою ціною виконувати числові вирази, не викидаючи помилок, як у наведеному вище прикладі. Це означає, що це також дозволяє складати функції. Що саме стосується монади - це правило складати функції, що відповідають аксіомам, визначеним на початку цієї відповіді.
Але чи є правилом функції складання, що випливає з реалізації JavaScript для роботи з числовими помилками, монадою?
Щоб відповісти на це запитання, все, що вам потрібно, це перевірити аксіоми (залишено як вправу, оскільки тут не є частиною запитання;).
Чи можна викид кидання використовувати для побудови монади?
Дійсно, більш корисною монадою було б замість цього правило, яке передбачає, що якщо f
викинути виняток для деяких x
, то і його склад з будь-яким g
. Плюс зробити виняток у E
всьому світі унікальним лише одним можливим значенням ( термінальний об'єкт у теорії категорій). Тепер дві аксіоми миттєво перевіряються, і ми отримуємо дуже корисну монаду. І результат - це те, що добре відоме як монада, можливо .