Gostaria de saber quais são as vantagens da Maybe
mônada sobre exceções? Parece que Maybe
é apenas uma maneira explícita (e bastante demorada) de try..catch
sintaxe.
atualização Observe que não intencionalmente não mencionei Haskell.
Gostaria de saber quais são as vantagens da Maybe
mônada sobre exceções? Parece que Maybe
é apenas uma maneira explícita (e bastante demorada) de try..catch
sintaxe.
atualização Observe que não intencionalmente não mencionei Haskell.
Usar Maybe
(ou o primo Either
que funciona basicamente da mesma maneira, mas permite retornar um valor arbitrário no lugar de Nothing
) serve a um propósito ligeiramente diferente das exceções. Em termos de Java, é como ter uma exceção verificada em vez de uma exceção de tempo de execução. Representa algo esperado com o qual você precisa lidar, em vez de um erro que você não esperava.
Portanto, uma função como indexOf
retornaria um Maybe
valor porque você espera a possibilidade de que o item não esteja na lista. É como retornar null
de uma função, exceto de uma maneira segura para o tipo, que o força a lidar com o null
caso. Either
funciona da mesma maneira, exceto que você pode retornar informações associadas ao caso de erro; portanto, é realmente mais semelhante a uma exceção que Maybe
.
Então, quais são as vantagens da abordagem Maybe
/ Either
? Por um lado, é um cidadão de primeira classe do idioma. Vamos comparar uma função usando Either
com uma que lança uma exceção. Para o caso de exceção, seu único recurso real é uma try...catch
declaração. Para a Either
função, você pode usar os combinadores existentes para tornar o controle de fluxo mais claro. Aqui estão alguns exemplos:
Primeiro, digamos que você queira tentar várias funções que podem ter erros consecutivos até que você obtenha uma que não funcione. Se você não obtiver nenhum erro, deseje retornar uma mensagem de erro especial. Este é realmente um padrão muito útil, mas seria uma dor horrível try...catch
. Felizmente, como Either
é apenas um valor normal, você pode usar as funções existentes para tornar o código muito mais claro:
firstThing <|> secondThing <|> throwError (SomeError "error message")
Outro exemplo é ter uma função opcional. Digamos que você tenha várias funções para executar, incluindo uma que tenta otimizar uma consulta. Se isso falhar, você deseja que todo o resto seja executado. Você pode escrever um código como:
do a <- getA
b <- getB
optional (optimize query)
execute query a b
Ambos os casos são mais claros e mais curtos que o uso try..catch
e, mais importante, mais semânticos. Usar uma função como <|>
ou optional
torna suas intenções muito mais claras do que usar try...catch
para sempre lidar com exceções.
Observe também que você não precisa desarrumar seu código com linhas como if a == Nothing then Nothing else ...
! O objetivo principal de tratar Maybe
e Either
como mônada é evitar isso. Você pode codificar a semântica de propagação na função de ligação para obter as verificações de nulo / erro gratuitamente. O único momento em que você precisa checar explicitamente é se deseja retornar algo diferente de Nothing
um a Nothing
, e mesmo assim é fácil: existem várias funções de biblioteca padrão para tornar esse código mais agradável.
Finalmente, outra vantagem é que um tipo Maybe
/ Either
é apenas mais simples. Não há necessidade de estender o idioma com palavras-chave adicionais ou estruturas de controle - tudo é apenas uma biblioteca. Como são apenas valores normais, isso simplifica o sistema de tipos - em Java, é necessário diferenciar entre tipos (por exemplo, o tipo de retorno) e efeitos (por exemplo, throws
instruções) onde você não usaria Maybe
. Eles também se comportam como qualquer outro tipo definido pelo usuário - não é necessário ter um código especial de tratamento de erros inserido no idioma.
Outra vitória é que Maybe
/ Either
são functores e mônadas, o que significa que eles podem tirar proveito das funções de fluxo de controle de mônadas existentes (das quais existe um número razoável) e, em geral, jogam bem junto com outras mônadas.
Dito isto, existem algumas ressalvas. Por um lado, Maybe
nem Either
substitua exceções não verificadas . Você desejará alguma outra maneira de lidar com coisas como dividir por 0 simplesmente porque seria doloroso fazer com que cada divisão retornasse um Maybe
valor.
Outro problema é o retorno de vários tipos de erros (isso se aplica apenas a Either
). Com exceções, você pode lançar diferentes tipos de exceções na mesma função. com Either
, você obtém apenas um tipo. Isso pode ser superado com a sub-digitação ou um ADT contendo todos os diferentes tipos de erros como construtores (essa segunda abordagem é a que geralmente é usada no Haskell).
Ainda assim, prefiro a abordagem Maybe
/ Either
porque acho mais simples e flexível.
OpenFile()
pode jogarFileNotFound
ouNoPermission
ouTooManyDescriptors
etc. A Nenhum não carrega essas informações.if None return None
instruções de estilo.Mais importante ainda, uma exceção e uma mônada Talvez têm objetivos diferentes - uma exceção é usada para significar um problema, enquanto uma Talvez não.
"Enfermeira, se houver um paciente na sala 5, você pode pedir para ele esperar?"
(observe o "se" - isso significa que o médico está esperando uma mônada Talvez)
fonte
None
valores podem ser apenas propagados). Seu ponto 5 é apenas meio certo ... a questão é: quais situações são inequivocamente excepcionais? Como se vê ... não muitos .bind
de tal maneira que o testeNone
não implique uma sobrecarga sintática no entanto. Um exemplo muito simples, o C # sobrecarrega osNullable
operadores adequadamente. Nenhuma verificação éNone
necessária, mesmo ao usar o tipo. É claro que a verificação ainda está concluída (é do tipo seguro), mas nos bastidores e não atrapalha seu código. O mesmo se aplica, em certo sentido, à sua objeção à minha objeção a (5), mas concordo que nem sempre isso se aplica.Maybe
como uma mônada é tornar a propagaçãoNone
implícita. Isso significa que, se você deseja retornar umNone
dadoNone
, não precisa escrever nenhum código especial. O único momento em que você precisa corresponder é se deseja fazer algo especialNone
. Você nunca precisa de umif None then None
tipo de afirmação.null
verificar exatamente assim (por exemploif Nothing then Nothing
) de graça, porqueMaybe
é uma mônada. Está codificado na definição de bind (>>=
) paraMaybe
.Either
) que se comportam da mesma maneiraMaybe
. Alternar entre os dois é realmente bastante simples, porqueMaybe
é realmente apenas um caso especial deEither
. (Em Haskell, você poderia pensarMaybe
comoEither ()
.)"Talvez" não substitui exceções. Exceções devem ser usadas em casos excepcionais (por exemplo: abrir uma conexão db e o servidor db não existe, embora deva existir). "Talvez" é para modelar uma situação em que você pode ou não ter um valor válido; digamos que você esteja obtendo um valor de um dicionário para uma chave: ele pode estar lá ou não - não há nada de "excepcional" em nenhum desses resultados.
fonte
Segundo a resposta de Tikhon, mas acho que há um ponto prático muito importante que todo mundo está perdendo:
Either
mecanismo não está acoplado a threads.Então, algo que estamos vendo hoje em dia na vida real é que muitas soluções de programação assíncronas estão adotando uma variante do
Either
estilo de manipulação de erros. Considere as promessas de Javascript , conforme detalhado em qualquer um desses links:O conceito de promessas permite que você escreva códigos assíncronos como este (extraídos do último link):
Basicamente, uma promessa é um objeto que:
Basicamente, como o suporte a exceções nativas do idioma não funciona quando a computação está acontecendo em vários segmentos, uma implementação promissora deve fornecer um mecanismo de tratamento de erros, e eles acabam sendo mônadas semelhantes aos tipos
Maybe
/ de HaskellEither
.fonte
O sistema do tipo Haskell exigirá que o usuário reconheça a possibilidade de a
Nothing
, enquanto as linguagens de programação geralmente não exigem que uma exceção seja capturada. Isso significa que saberemos, em tempo de compilação, que o usuário verificou um erro.fonte
throws NPE
a cada assinatura ecatch(...) {throw ...}
a cada corpo de método. Mas acredito que há um mercado para checagem no mesmo sentido que em Talvez: a nulidade é opcional e rastreada no sistema de tipos.Talvez a mônada seja basicamente o mesmo que o uso mais comum da linguagem da verificação de "nulo significa erro" (exceto que exige que o nulo seja verificado) e possui as mesmas vantagens e desvantagens.
fonte
Maybe
números escrevendoa + b
sem a necessidade de verificarNone
, e o resultado é mais uma vez um valor opcional.Maybe
tipo , mas o usoMaybe
como mônada adiciona açúcar de sintaxe que permite expressar a lógica de nulidade muito mais elegante.O tratamento de exceções pode ser uma verdadeira dor de fatoração e teste. Eu sei que o python fornece boa sintaxe "com" que permite interceptar exceções sem o rígido bloco "try ... catch". Mas em Java, por exemplo, os blocos try catch são grandes, padronizados, detalhados ou extremamente detalhados e difíceis de quebrar. Além disso, o Java adiciona todo o ruído em torno das exceções verificadas versus não verificadas.
Se, em vez disso, sua mônada captura exceções e as trata como uma propriedade do espaço monádico (em vez de alguma anomalia de processamento), você pode misturar e combinar funções vinculadas a esse espaço, independentemente do que elas lançam ou capturam.
Se, melhor ainda, sua mônada evita condições em que exceções podem ocorrer (como enviar uma verificação nula para Maybe), é ainda melhor. se ... então é muito, muito mais fácil fatorar e testar do que tentar ... capturar.
Pelo que vi, Go está adotando uma abordagem semelhante, especificando que cada função retorna (resposta, erro). É o mesmo que "elevar" a função para um espaço de mônada, em que o tipo de resposta principal é decorado com uma indicação de erro e efetivamente lança exceções.
fonte