Добре, перше правило поводження з помилками в Haskell: Ніколи не використовуйтеerror
.
Це просто страшно в усіх відношеннях. Він існує суто як акт історії, і те, що Прелюдія використовує його, є жахливим. Не використовуйте його.
Єдиний можливий час, який ви могли б використати, це коли щось настільки жахливо внутрішньо, що щось має бути не так із самою тканиною реальності, тим самим виводячи результат програми.
Тепер питання стає Maybe
проти Either
. Maybe
добре підходить для чогось подібного head
, що може або не може повернути значення, але є лише одна можлива причина, щоб не вдатися. Nothing
говорить щось на кшталт "зламалося, і ти вже знаєш чому". Дехто би сказав, що це вказує на часткову функцію.
Найбільш надійною формою обробки помилок є Either
+ ADT про помилку.
Наприклад, в одному зі своїх хобі-компіляторів у мене є щось подібне
data CompilerError = ParserError ParserError
| TCError TCError
...
| ImpossibleError String
data ParserError = ParserError (Int, Int) String
data TCError = CouldntUnify Ty Ty
| MissingDefinition Name
| InfiniteType Ty
...
type ErrorM m = ExceptT CompilerError m -- from MTL
Тепер я визначаю купу типів помилок, вкладаючи їх так, щоб у мене була одна славна помилка верхнього рівня. Це може бути помилка на будь-якій стадії компіляції або ImpossibleError
, що означає помилку компілятора.
Кожен з цих типів помилок намагається зберегти якомога довшу інформацію для гарного друку чи іншого аналізу. Що ще важливіше, не маючи рядка, я можу перевірити, що запуск нетипової програми через перевірку типу насправді створює помилку об’єднання! Після того, як щось є String
, воно назавжди зникне, і будь-яка інформація, що міститься, непрозора для компілятора / тестів, тому просто теж Either String
не чудово.
Нарешті я пакую цей тип у ExceptT
новий трансформатор монади від MTL. Це по суті EitherT
і постачається з приємною партією функцій для кидання та лову помилок чистим, приємним способом.
Нарешті, варто згадати, що Haskell має механізми для підтримки винятків, як інші мови, за винятком випадків, коли відбувається виняток IO
. Я знаю, що деякі люди люблять використовувати їх для IO
важких програм, де все потенційно може вийти з ладу, але настільки рідко, що вони не люблять думати про це. Чи використовуєте ви ці нечисті винятки чи просто ExceptT Error IO
справді - справа смаку. Особисто я вибираю, ExceptT
тому що мені подобається нагадувати про шанс невдачі.
Як підсумок,
Maybe
- Я можу провалитися одним очевидним способом
Either CustomType
- Я можу зазнати невдачі, і я розповім тобі, що сталося
IO
+ винятки - я часом не вдається. Перевірте мої документи, щоб побачити, що я кидаю, коли це роблю
error
- Я ненавиджу вас, також, користувача
head
або,last
здається, використовуютьсяerror
, тому мені було цікаво, чи це насправді хороший спосіб робити справи, і мені просто щось не вистачало. Це відповідає на це питання. :)