#haskell #monad-transformers #state-monad
#haskell #монады-трансформеры #состояние-монада
Вопрос:
Допустим, у меня есть следующие функции:
checkA :: a -> Either err b
checkA = undefined
checkB :: b -> ST s (Either err c)
checkB = undefined
check :: a -> ST s (Either err c)
check a = either (return . Left) checkB (checkA a)
Есть ли какой-нибудь способ написать check
так, чтобы это не требовало использования return . Left
? Обычно я бы сделал что-то вроде >>=
, но в этом случае возврат checkB
заключен в другую монаду состояния, поэтому это не работает. Другое ограничение заключается в том, что checkB
должно выполняться, только если checkA a
вычисляется Right
, и должно просто завершиться ошибкой при Left
Обобщая, существуют ли какие-либо стандартные подходы к использованию вложенных монад?
Комментарии:
1. Непонятно, что вы пытаетесь сделать. Вы действительно собираетесь использовать конкретные типы ‘A’, ‘B’ и ‘Err’? Если это так, то ваша
check
функция не будет проверять тип.2. Если вы хотите избавиться от
either
вместоreturn
, я думаю,check = (return . checkA) >=> checkB
это эквивалентно.3. @theindigamer Я обновлю свой вопрос. Я действительно хочу использовать конкретные типы, но на самом деле это
A
,B
,C
иErr
. Использование дженериков здесь неясно4. Вы ищете контроль. Монада. Перевод. Кроме? Если бы у вас был
ExceptT err (ST s) a
вместо того, чтобы составлять монады вручную, вы могли бы написатьthrowE
вместоreturn . Left
.5. @amalloy это, безусловно, выглядит лучше. Мне просто кажется, что всякий раз, когда у меня возникает что-то вроде
either
, когда я не занимаюсь одной стороной, должен быть лучший способ написания вещей. Даже сeither throwE ...
это может быть избыточным. Я пытаюсь найти общее решение с вложенными монадами, где приведенный выше фрагмент является моим фактическим использованием. Другой пример — если бы у меня была функция, имеющая дело сEither a (Maybe b)
Ответ №1:
Вот один из способов сделать это с ExceptT
:
checkA :: a -> Either err b
checkA = undefined
checkB :: b -> ExceptT err (ST s) c
checkB = undefined
check :: a -> ExceptT err (ST s) c
check a = except (checkA a) >>= checkB
-- or
check = except . checkA >=> checkB
except
превращается Either err b
в Monad m => ExceptT err m b
, и тогда вы можете делать все остальное в ExceptT err (ST s)
монаде.
Как правило, ExceptT
это отличный способ работать с монадическими действиями, которые могут завершиться неудачей, когда вы обычно хотите избежать сбоя. Основное исключение — это когда базовая монада является IO
, и в этом случае чаще всего используются встроенные функции исключения из Control.Exception
.
Конечно, если вам нужна только одна монадическая привязка, ExceptT
это немного похоже на перебор, но как только вам понадобится больше, это определенно имеет смысл.