Композиция Клейсли на самом деле является одним из самых простых способов ответить на часто задаваемый вопрос: для чего нужны монады?
Одна из самых полезных вещей, которые мы можем сделать с помощью обычных функций, - это их составление. Учитывая f :: a -> b
и g :: b -> c
, мы можем сначала выполнить f
, а затем g
для результата, что даст нам g . f :: a -> c
.
Это фантастика c, пока нам остается работать с "обычными" функциями. Но как только мы начнем программировать в «реальном мире», мы, скорее всего, столкнемся с ситуациями, когда мы не сможем продолжать использовать такие функции, если наш язык останется чистым и ссылочно-прозрачным. Действительно, в таких ситуациях другие языки, которые менее принципиальны, чем Haskell, отказываются от любых притязаний быть чистыми. Рассмотрим следующие повседневные ситуации:
- наша функция
f
может иногда не возвращать значение. Во многих других языках это будет обозначаться возвращением null
, но вы не можете затем вставить его в g
. (Конечно, вы можете адаптировать g
, чтобы справиться с null
входами, но это быстро станет повторным.)
В Haskell у нас нет null
с, у нас есть конструктор типа Maybe
, чтобы явно указать, что может не быть значения. Это означает, что f
должен иметь тип a -> Maybe b
. g
будет иметь тип b -> Maybe c
по той же причине. Но при этом мы утратили способность объединять две функции, поскольку мы не можем напрямую передать значение типа Maybe b
тому, которое ожидает ввода типа b
.
- результат
f
может зависеть от некоторых побочных эффектов (например, ввода от пользователя или результата запроса к базе данных). В нечистых языках это не проблема, но в Haskell, чтобы сохранить чистоту, мы должны реализовать это в виде функции типа a -> IO b
. Еще раз, g
получит ту же форму, b -> IO c
, и мы утратили способность наивно составлять две функции.
Я уверен, что вы видите, куда это идет. В обоих случаях (и более легко можно было бы предоставить по одному для каждой монады) нам пришлось заменить простую функцию типа a -> b
на функцию типа a -> m b
, чтобы учесть конкретный тип «побочного эффекта» - или, если хотите, какой-то особый вид «контекста», который применяется к результату функции. И при этом мы теряем способность составлять две функции, которые у нас были в мире «без побочных эффектов».
Для чего монады действительно нужны, так это для преодоления этого, и давайте восстановим форму композиции для такие "нечистые функции". Это, конечно, именно то, что дает нам композиция Клейсли, композиция функций вида a -> m b
, которая удовлетворяет в точности тем свойствам, которые мы ожидаем от композиции функций (а именно, ассоциативности, и «функции идентичности» для каждого типа, которая здесь равна * 1044). *).
Ваше предложение "не совсем композиции" типа (a -> m b) -> (b -> m c) -> (m a -> m c)
просто не было бы полезным, поскольку часто получаемой функции требуется значение monadi c в качестве входных данных, когда основной способ получения монади c значений на практике возникает как вывод с. Вы все еще можете сделать это, когда вам нужно, просто взяв «правильную» композицию Клейсли и передав ей значение монади c через >>=
.