Either и обработка ошибок

Either — это Maybe, который умеет объяснить, что пошло не так. Вместо безмолвного Nothing — сообщение об ошибке.
Right x — успех со значением, Left err — провал с описанием. Имена легко запомнить: «right» ещё значит «правильно».

Тип Maybe говорит «значения нет», но не объясняет почему. Когда причина важна, используют Either:

data Either a b = Left a | Right b

По соглашению Right b хранит успешный результат, а Left a — информацию об ошибке (часто строку или свой тип ошибки). Подсказка для памяти: right — это и «правый», и «правильный».

safeDivE :: Int -> Int -> Either String Int
safeDivE _ 0 = Left "деление на ноль"
safeDivE x y = Right (x `div` y)
safeDivE 10 2  ->  Right 5            (успех)
safeDivE 10 0  ->  Left "деление..."  (ошибка С ПРИЧИНОЙ)

Разбор результата

Оба случая обрабатывают сопоставлением с образцом:

report :: Either String Int -> String
report (Left err) = "Ошибка: " ++ err
report (Right v)  = "Успех: "  ++ show v

-- report (safeDivE 10 0)  ==  "Ошибка: деление на ноль"
-- report (safeDivE 10 2)  ==  "Успех: 5"

Это типобезопасная альтернатива исключениям: путь ошибки виден прямо в типе, и его невозможно «не заметить». Функция честно объявляет, что может вернуть и успех, и провал с пояснением.

В Python такого встроенного типа нет, но идею «успех или ошибка как значение» можно смоделировать кортежем или классом:

# Та же идея на Python: результат как значение, а не исключение
def safe_div(x, y):
    if y == 0:
        return ("error", "деление на ноль")   # ~ Left
    return ("ok", x // y)                       # ~ Right

def report(result):
    tag, payload = result
    if tag == "error":
        return f"Ошибка: {payload}"
    return f"Успех: {payload}"

print(report(safe_div(10, 2)))   # Успех: 5
print(report(safe_div(10, 0)))   # Ошибка: деление на ноль

Ошибки как обычные значения

Главное мировоззренческое отличие Either от исключений в том, что ошибка перестаёт быть чем-то выпрыгивающим из-за угла и становится обычным значением, которое функция честно возвращает. Путь успеха и путь ошибки оба видны прямо в типе, и компилятор требует обработать каждый — нельзя «случайно» проигнорировать неудачу, как легко забыть поймать исключение. Это делает обработку ошибок предсказуемой и локальной: вы видите, где она возникает и где обрабатывается, не гадая, какой обработчик перехватит брошенное где-то глубоко исключение. Для серьёзного кода в Left кладут не голую строку, а собственный тип ошибки — перечисление возможных причин, которое и документирует, и позволяет реагировать на разные случаи по-разному. А поскольку Either — монада, цепочки операций можно связывать так, что первая же ошибка коротко замыкает вычисление, аккуратно прерывая дальнейшие шаги.

Как это мыслить

Думайте об Either как о «двух дорожках»: успешные данные едут по правой (Right), ошибки — по левой (Left). Вместо того чтобы выбрасывать исключение и надеяться, что кто-то его поймает, вы возвращаете результат, который честно описывает оба исхода прямо в типе.

Полезно знать про практическую деталь: ветка Left в Either по соглашению несёт ошибку именно потому, что монада Either «коротко замыкается» на Left, прерывая цепочку, ровно как Maybe прерывается на Nothing. Это делает Either естественным выбором для последовательности шагов, каждый из которых может провалиться с объяснением: первая же ошибка останавливает вычисление и доносит свою причину до конца. Для крупных приложений на этой идее строят целые системы обработки ошибок, где тип ошибки тщательно проектируют как отдельное перечисление. Но даже в маленьких программах привычка возвращать Either вместо выбрасывания исключений окупается: пути отказа становятся видимыми в типах, документированными и невозможными к случайному игнорированию.

Частые ошибки

  • Перепутать Left и Right. Запомните: Right — успех (правильно), Left — ошибка.
  • Класть в Left то же, что в Right. Тип ошибки и тип успеха обычно разные.
  • Игнорировать ветку Left. Компилятор напомнит обработать ошибку — так и задумано.

Best practices

  • Используйте Either, когда важна причина ошибки; Maybe — когда достаточно факта «нет значения».
  • Для ошибок заводите осмысленный тип (своё перечисление), а не только строку.
  • Связывайте цепочки операций монадически (следующий урок), чтобы ошибка «коротко замыкала» вычисление.

Итог. Either a b — это Left a (ошибка с описанием) или Right b (успех). Это типобезопасная обработка ошибок без исключений: оба исхода видны в типе, и компилятор требует учесть каждый.

Проверьте себя
1. Что по соглашению означают Left и Right в Either?
ALeft — успех, Right — ошибка
BLeft — ошибка (с описанием), Right — успешный результат
CОба означают успех
DLeft — число, Right — строка
2. Когда стоит выбрать Either вместо Maybe?
AКогда работаем только с числами
BКогда важно знать причину неудачи, а не только факт её наличия
CКогда нужен более быстрый код
DEither и Maybe полностью взаимозаменяемы