Что такое канал ошибок ZIO и как понять, что в него вставлять?

ZIO (https://zio.dev/) - это среда Scala, в основе которой лежит структура данных ZIO[R, E, A] и ее сайт предоставляет следующую информацию по трем параметрам:

ZIO

Тип данных ZIO[R, E, A] имеет три параметра типа:

  • R - Тип среды. Эффект требует окружения типа R. Если этот параметр типа - Any, это означает, что эффект не имеет требований, потому что вы можете запустить эффект с любым значением (например, единицей измерения ()).
  • E - Тип ошибки. Эффект может не работать со значением типа E. Некоторые приложения будут использовать Throwable. Если этот параметр типа - Nothing, это означает, что эффект не может потерпеть неудачу, потому что нет значений типа Nothing.
  • A - Тип успеха. Эффект может быть успешным со значением типа A. Если этот параметр типа - Unit, это означает, что эффект не дает полезной информации, а если он равен Nothing, это означает, что эффект будет работать вечно (или до отказа).

Легко понять, что такое A: это значение, возвращаемое функцией в номинальном случае, то есть почему мы закодировали функцию для. R - это своего рода инъекция зависимостей - интересная тема, но мы можем просто игнорировать ее, чтобы использовать ZIO, всегда устанавливая для нее значение Any (а на самом деле в библиотеке есть псевдоним IO[E, A] = ZIO[Any, E, A]).

Таким образом, остается тип E, который предназначен для ошибки (знаменитый канал ошибок). Я почти понимаю, что IO[E, A] - это что-то вроде Either[E, A], но имеет дело с эффектом (и это здорово).

У меня вопрос: почему я должен использовать канал ошибок ВЕЗДЕ в моем приложении и как я могу решить, что должно быть в канале ошибок?


person fanf42    schedule 02.09.2020    source источник


Ответы (1)


1 / Зачем использовать управление с каналом ошибки?

Одна из самых сложных задач разработчика - решить, что является ошибкой, а что нет в вашем приложении - или, точнее, выявить режимы отказа: каков номинальный путь (то есть цель этого кода), что является ожидаемым. ошибка, с которой приложение может как-то справиться позже, и какие непредвиденные ошибки приложение не может исправить. На этот вопрос нет однозначного ответа, он зависит от приложения и контекста, поэтому решать вам, разработчику.

Но самая сложная задача - создать приложение, которое выполняет свои обещания (ваши обещания, поскольку вы выбрали, что является ошибкой и каков номинальный путь), и это неудивительно, так как пользователи, администраторы и разработчики - включая будущее, в котором вы находитесь. две недели - знать, что делает код в большинстве случаев, не догадываясь, и иметь агентство, чтобы адаптироваться к этому поведению, в том числе реагировать на ошибки.

Это сложно, и вам нужен систематический процесс, чтобы разобраться со всеми возможными случаями без всяких сомнений.

Канал ошибок в IO бимонаде (и, следовательно, ZIO) помогает вам в этой задаче: IO монада помогает отслеживать эффекты, которые являются источником большинства ошибок, а канал ошибок явно указывает возможные случаи ошибок, и поэтому у других частей приложения есть агентство, которое может с ними разобраться, если они могут. Вы сможете управлять своими эффектами в чистом, последовательном, компонуемом виде с явными режимами отказа. Более того, в случае ZIO вы можете легко импортировать нечистый код, такой как устаревшая java, очень легко:

val pure = ZIO.effect(someJavaCodeThrowingException)

2 / Как выбрать, что является ошибкой?

Таким образом, канал ошибок предоставляет способ закодировать ответ на what if? вопрос для будущего разработчика, работающего над этим кодом. Что делать, если база данных не работает? есть DatabaseConnectionError. Но все what if не одинаковы для ВАШЕГО варианта использования, для ТЕКУЩЕГО уровня приложения. Что делать, если пользователь не найден? - ах, это может быть полностью ожидаемый ответ на нижнем уровне репозитория (например, находка, которая ничего не нашла), или это может быть ошибка на другом уровне (например, когда вы находитесь в процессе аутентификации пользователя , он действительно должен быть там). В первом случае вы, скорее всего, не будете использовать канал ошибок: это номинальный путь, иногда вы ничего не находите. А во втором случае вы, вероятно, будете использовать канал ошибок (UserNotFoundError).

Итак, как мы уже говорили, ошибки в канале ошибок обычно связаны с what if вопросом, с которым вы, возможно, захотите иметь дело в приложении, но не на этом функциональном уровне. Первый пример DatabaseConnectionError может быть обнаружен выше в приложении и привести к пользовательскому сообщению, например, попробуйте еще раз, и электронному письму с уведомлением системному администратору (быстро, посмотрите, здесь что-то не так). UserNotFoundError, вероятно, будет обрабатываться как сообщение об ошибке для пользователя в форме входа, что-то вроде неверного входа в систему или пароля, попробуйте еще раз или восстановить учетные данные с помощью этого процесса.

Так что эти случаи (номинальные и ожидаемые ошибки) - самые простые части. Но есть некоторые what if вопросы, на которые ваше приложение, независимо от уровня, не знает, как ответить. Что делать, если я получаю исключение памяти при попытке выделить этот объект? Я понятия не имею, и на самом деле, даже если бы у меня был ключ, это выходит за рамки того, что я хотите иметь дело с этим приложением. Таким образом, эти ошибки НЕ попадают в канал ошибок. Мы называем их сбоями и выкидываем приложение, когда они случаются, потому что вполне вероятно, что приложение сейчас находится в неизвестном, опасном, зомби-состоянии.

Опять же, этот выбор (номинальный путь / канал ошибки / сбой) - ваш выбор: два приложения могут делать разные выборы. Например, одноразовое приложение-обработка-данные-затем-сбросить-скорее всего будет рассматривать все не номинальные пути как сбои. Есть разработчик, который поймает этот случай в реальном времени и решит, насколько он важен (см. Shell, Python и любые сценарии, в которых эта стратегия активно используется - хорошо, иногда даже когда нет разработчика, который мог бы отлавливать ошибки :). С другой стороны, разработчик NASA поместил ВСЕ в канал ошибок (+), даже ПОВРЕЖДЕНИЕ памяти. Поскольку это ожидаемая ошибка, приложение должно знать, как с ней бороться, и продолжить.

(+) ПРИМЕЧАНИЕ: AFAIK они не используют zio (на данный момент), но процесс принятия решения о том, что является ошибкой, такой же, даже в C.

Чтобы пойти дальше, я (@ fanf42) выступил с докладом на конференции Scala.io. Доклад «Систематическое управление ошибками в приложении» доступен на французском здесь < / а>. Да, французский, я знаю, но слайды доступны на английском здесь! И вы можете позвонить мне (см. Контактную информацию в конце слайда).

person fanf42    schedule 02.09.2020