Обработка монад Either и ST

#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 это немного похоже на перебор, но как только вам понадобится больше, это определенно имеет смысл.