Насколько я могу судить, одним из основных применений операции монадического связывания является подстановка переменных, например
do x <- maybeComputeSomething
y <- maybeComputeSomethingElse
maybeDoStuff x y
Это все хорошо, если MaybeComputeSomething, MaybeComputeSomethingElse и MaybeDoStuff все возвращают значения Maybe, но я чувствую, что такая форма многократного замещения будет полезна в вычислениях, даже если не было возможности вернуть ничего. Поскольку личность является монадой, я бы в некотором смысле ожидал, что смогу что-то сделать, например
do x <- computeSomething
y <- computeSomethingElse
doStuff x y
, который расширился бы до чего-то похожего на
computeSomething >>= (\x ->
computeSomethingElse >>= (\y ->
doStuff x y))
и поскольку связывание в монаде тождества просто x >> = f = f x, это будет действовать как
doStuff computeSomething computeSomethingElse
if >> = рассматривались как привязка идентификационной монады, но это (неудивительно) терпит неудачу, потому что в конце концов я никогда не указывал явно монаду (в отличие от примера Maybe, когда типы явно имеют форму Maybe a) и если Haskell Предполагалось, что монада идентичности везде, где большинство случаев достаточно быстро загромождается необходимыми неоднозначностями.
Так что это подводит меня к моему вопросу. Я хочу код, который выглядит как
do x <- computeSomething
y <- computeSomethingElse
doStuff x y
и это то же самое, что и
doStuff computeSomething computeSomthingElse
Я понимаю, что я мог бы сделать это, явно определив монаду Id, которая выглядит как «Может быть», но без возможности «Ничего», но это принимает типы a -> Id a, и, таким образом, на самом деле не определяет монаду идентификации. Кроме того, мои функции теперь должны иметь типы a -> Id b, тогда как мне бы очень хотелось, чтобы они по-прежнему имели форму a -> b. Есть ли способ создать ощущение повторной подстановки, не внося сложности в задействованные типы?