Что такое канал ошибок ZIO и как понять, что в него вставлять?
ZIO
(https://zio.dev/) - это фреймворк Scala, в основе которого лежитZIO[R, E, A]
datastructure и ее сайт предоставляют следующую информацию по трем параметрам:
ЗИО
В
ZIO[R, E, A]
Тип данных имеет три параметра типа:
R
- Тип среды. Эффект требует окружения типаR
. Если этот параметр типаAny
, это означает, что эффект не имеет требований, потому что вы можете запустить эффект с любым значением (например, значением единицы()
).E
- Тип отказа. Эффект может не работать со значением типаE
. Некоторые приложения будут использоватьThrowable
. Если этот параметр типаNothing
, это означает, что эффект не может потерпеть неудачу, потому что нет значений типа Nothing.A
- Тип успеха. Эффект может быть успешным со значением типаA
. Если этот параметр типаUnit
, это означает, что эффект не дает полезной информации, а если онNothing
, это означает, что эффект длится вечно (или до отказа).
Легко получить то, что A
is: это значение, возвращаемое функцией в номинальном случае, т.е. почему мы закодировали функцию для.R
это своего рода инъекция зависимостей - интересная тема, но мы можем просто игнорировать ее, чтобы использовать ZIO
всегда устанавливая его на Any
(и на самом деле есть IO[E, A] = ZIO[Any, E, A]
псевдоним в библиотеке).
Итак, остается E
type, обозначающий ошибку (известный канал ошибок). Я очень понимаю этоIO[E, A]
это своего рода Either[E, A]
, но имеет дело с эффектом (что здорово).
У меня вопрос: почему я должен использовать канал ошибок ВЕЗДЕ в моем приложении и как я могу решить, что должно быть в канале ошибок?
1 ответ
1/ Зачем использовать управление с ошибочным каналом?
Одна из самых сложных задач разработчика - решить, что является ошибкой, а что нет в вашем приложении - или, точнее, выявить режимы отказа: каков номинальный путь (т.е. цель этого кода), что является ожидаемым. ошибка, с которой приложение может как-то справиться позже, и какие неожиданные ошибки приложение не может исправить. На этот вопрос нет однозначного ответа, он зависит от приложения и контекста, и поэтому решать вам, разработчику.
Но самая сложная задача - создать приложение, которое выполняет свои обещания (ваши обещания, поскольку вы выбрали, что является ошибкой и какой номинальный путь), и это неудивительно, так как пользователи, администраторы и разработчики, включая будущее, в котором вы две недели - знать, что делает код в большинстве случаев, без необходимости угадывать, и иметь агентство, чтобы адаптироваться к этому поведению, в том числе реагировать на ошибки.
Это сложно, и вам нужен систематический процесс, чтобы разобраться со всеми возможными случаями без всяких нареканий.
Канал ошибки в IO
бимонада (и таким образом ZIO
) поможет вам в решении этой задачи: IO
монада помогает вам отслеживать эффекты, которые являются источником большинства ошибок, а канал ошибок показывает возможные случаи ошибок, и поэтому у других частей приложения есть агентство, которое может справиться с ними, если они могут. Вы сможете управлять своими эффектами чистым, последовательным, компонуемым способом с явными режимами отказа. Более того, в случаеZIO
, вы можете легко импортировать не чистый код, такой как устаревшая Java:
val pure = ZIO.effect(someJavaCodeThrowingException)
2/ Как выбрать ошибку?
Таким образом, канал ошибок предоставляет способ кодирования ответа на what if?
вопрос к Futur Dev, работающему над этим кодом. "Что делать, если база данных не работает?" "естьDatabaseConnectionError
". Но все what if
не одинаковы для ВАШЕГО варианта использования, для ТЕКУЩЕГО уровня приложения. "Что делать, если пользователь не найден?" - ах, это может быть полностью ожидаемый ответ на низком уровне "репозитория" (например, "находка", которая ничего не нашла), или это может быть ошибка на другом уровне (например, когда вы находитесь в процессе аутентификации пользователя, он действительно должен быть там). В первом случае вы, скорее всего, не будете использовать канал ошибок: это номинальный путь, иногда вы ничего не находите. А во втором случае вы, вероятно, будете использовать канал ошибок (UserNotFoundError
).
Итак, как мы уже говорили, ошибки в канале ошибок обычно связаны с what if
вопрос, который вы, возможно, захотите решить в приложении, но не на этом функциональном уровне. Первый примерDatabaseConnectionError
может быть обнаружен выше в приложении и приведет к появлению сообщения пользователя типа "попробуйте еще раз" и электронного письма с уведомлением системному администратору ("быстро, посмотрите, здесь что-то не так"). ВUserNotFoundError
скорее всего, будет обрабатываться как сообщение об ошибке для пользователя в форме входа, что-то вроде "неверный логин или пароль, попробуйте еще раз или восстановите учетные данные с помощью этого процесса".
Так что эти случаи (номинальные и ожидаемые ошибки) - самые простые части. Но есть некоторыеwhat if
вопросы, на которые ваше приложение, независимо от его уровня, не знает, как ответить. "Что, если я получаю исключение памяти при попытке выделить этот объект?" Я понятия не имею, и на самом деле, даже если бы я имел представление, это выходит за рамки того, чем я хочу заниматься для этого приложения. Таким образом, эти ошибки НЕ попадают в канал ошибок. Мы называем их сбоями и аварийно завершаем работу приложения, когда они происходят, потому что вполне вероятно, что приложение сейчас находится в неизвестном, опасном состоянии зомби.
Опять же, этот выбор (номинальный путь / канал ошибки / отказ) - ваш выбор: два приложения могут делать разные выборы. Например, одноразовое приложение-обработка-данные-затем-сбросить-скорее всего будет рассматривать все не номинальные пути как сбои. Есть разработчик, который может поймать этот случай в реальном времени и решить, важно ли это (см. Shell, Python и любые сценарии, в которых эта стратегия активно используется - хорошо, иногда даже когда нет разработчика, который мог бы вылавливать ошибки:). С другой стороны, разработчик НАСА поместил ВСЕ в канал ошибок (+), даже ПОВРЕЖДЕНИЕ памяти. Поскольку это ожидаемая ошибка, приложение должно знать, как с этим бороться, и продолжить.
(+) ПРИМЕЧАНИЕ: AFAIK они не используют zio (на данный момент), но процесс принятия решения о том, что является ошибкой, такой же, даже в C.
Чтобы пойти дальше, я (@fanf42) выступил с докладом на конференции Scala.io. Доклад "Систематическое управление ошибками в приложениях" доступен на французском языке здесь. Да, французский, я знаю, но слайды на английском здесь есть! И вы можете связаться со мной (см. Контактную информацию в конце слайдов).