Tudo bem, primeira regra de tratamento de erros no Haskell: nunca useerror
.
É simplesmente terrível em todos os sentidos. Ele existe puramente como um ato histórico e o fato de o Prelude usá-lo é terrível. Não use.
O único momento concebível que você pode usar é quando algo é tão terrível internamente que algo deve estar errado com o próprio tecido da realidade, tornando assim o resultado do seu programa discutível.
Agora, a questão torna-se Maybe
vs Either
. Maybe
é adequado para algo como head
, que pode ou não retornar um valor, mas há apenas uma razão possível para falhar. Nothing
está dizendo algo como "quebrou e você já sabe o porquê". Alguns diriam que indica uma função parcial.
A forma mais robusta de tratamento de erros é Either
+ um erro ADT.
Por exemplo, em um dos meus compiladores de hobby, tenho algo como
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
Agora, defino vários tipos de erros, aninhando-os para que eu tenha um glorioso erro de nível superior. Isso pode ser um erro de qualquer estágio da compilação ou de umImpossibleError
, o que significa um erro do compilador.
Cada um desses tipos de erro tenta manter o máximo de informações pelo maior tempo possível para uma impressão bonita ou outra análise. Mais importante, por não ter uma string, posso testar se a execução de um programa digitado por meio do verificador de tipos realmente gera um erro de unificação! Quando algo é um String
, desapareceu para sempre e qualquer informação contida é opaca para o compilador / testes, então Either String
também não é ótimo.
Finalmente, eu embalo esse tipo em ExceptT
um novo transformador de mônada da MTL. Isto é essencialmenteEitherT
e vem com um bom lote de funções para gerar e capturar erros de uma maneira pura e agradável.
Por fim, vale mencionar que Haskell tem os mecanismos para oferecer suporte ao tratamento de exceções, como outras linguagens, exceto que a captura de uma exceção vive IO
. Sei que algumas pessoas gostam de usá-las para IO
aplicativos pesados, onde tudo pode potencialmente falhar, mas com tanta frequência que não gostam de pensar nisso. Se você usa essas exceções impuras ou apenas ExceptT Error IO
é realmente uma questão de gosto. Pessoalmente, opto ExceptT
porque gosto de ser lembrado da chance de fracasso.
Como resumo,
Maybe
- Eu posso falhar de uma maneira óbvia
Either CustomType
- Eu posso falhar, e eu vou te contar o que aconteceu
IO
+ exceções - às vezes falho. Verifique meus documentos para ver o que eu jogo quando faço
error
- Eu também te odeio usuário
head
oulast
parecem usarerror
, então fiquei pensando se essa seria realmente uma boa maneira de fazer as coisas e que estava faltando alguma coisa. Isso responde a essa pergunta. :)Eu não separaria 2 e 3 com base em quantas maneiras algo pode falhar. Assim que você achar que há apenas uma maneira possível, outra mostrará sua face. A métrica deveria ser "meus chamadores se importam por que algo falhou? Eles podem realmente fazer algo sobre isso?".
Além disso, não está imediatamente claro para mim que
Either String SomeType
pode produzir uma condição de erro. Eu faria um tipo de dados algébrico simples com as condições com um nome mais descritivo.O uso que você usa depende da natureza do problema que você enfrenta e das expressões idiomáticas do pacote de software com o qual você está trabalhando. Embora eu tenderia a evitar o # 1.
fonte
Either
erros é um padrão muito conhecido no Haskell.Either
não é a parte pouco clara, é o uso deString
um erro e não uma string real.