Tratamento de Exceções F#

Como desenvolvedor, usar o tratamento de exceção do F# é um aspecto importante da escrita de código robusto. O tratamento de exceções permite lidar com erros inesperados e exceções que podem ocorrer durante a execução de seu programa.

Neste artigo, exploraremos as práticas recomendadas de manipulação de exceção F# e forneceremos dicas sobre como gerar e capturar exceções, lidar com diferentes tipos de exceções e práticas recomendadas quando se trata de escrever um código de manipulação de exceção que seja eficiente, sustentável e eficaz.



Compreendendo exceções em F#

Em F#, as exceções são geradas quando ocorre um erro durante a execução de um programa.

Uma exceção é um evento de tempo de execução que interrompe o fluxo normal de execução do programa e pode ocorrer por vários motivos, como entrada inválida, comportamento inesperado ou estado inesperado.

Quando uma exceção é gerada, o fluxo normal do programa é interrompido e o tempo de execução procura um manipulador de exceção que possa lidar com a exceção. Se um manipulador de exceção apropriado não for encontrado, o programa será encerrado.

O conceito de exceção é o termo usado para descrever um problema que ocorre quando um programa está sendo executado. Exceções de F# são geradas quando um programa está sendo executado quando uma circunstância excepcional surge durante a execução do programa, como dividir por zero quando o programa está em execução.

O uso de exceções fornece um método pelo qual uma parte do programa pode transferir o controle para outra parte.

As seguintes construções estão disponíveis para lidar com exceções em F# :

Construir Descrição
aumentar expr A exceção fornecida é levantada.
falha com expr A exceção System.Exception é levantada.
tente expr com regras Pegue as expressões correspondentes de acordo com as regras do padrão.
tente expr finalmente expr A expressão final é definida para ser executada tanto no caso de uma computação bem-sucedida quanto no caso de uma exceção.
| 😕ArgumentException Uma regra de exceção que corresponde ao tipo de exceção especificado pela estrutura .NET.
| 😕ArgumentException como e A regra vincula o nome “e” ao valor de objeto de um objeto de exceção que corresponda ao tipo de exceção .NET fornecido.
| Falha(mensagem) → expr Uma regra que corresponde a uma exceção F# de transporte de dados que foi fornecida.
| exn → expr Uma regra de exceção que corresponde a qualquer exceção e vincula o nome exn ao valor do objeto de exceção dessa exceção.
| exn quando expr → expr Uma regra de correspondência de exceção que liga o nome exn ao valor do objeto de exceção sob as condições dadas .

Para entender como o Tratamento de Exceções funciona, vamos começar com a sintaxe básica.

Sintaxe

No F#, a sintaxe para lidar com exceções é a seguinte:

exception exception-type of argument-type

Onde,

  • tipo de exceção introduz um novo tipo de exceção em F#.
  • tipo de argumento esse tipo pode ser especificado por tipo de argumento ao gerar uma exceção.
  • As tuplas podem ser usadas para o tipo de argumento quando vários argumentos são necessários.

F# usa a expressão try…with para lidar com exceções.

Aqui está a sintaxe para um try … com expressão:

try
   expression1
with
   | pattern1 -> expression2
   | pattern2 -> expression3
...

A expressão try…finally pode ser usada para executar código de limpeza mesmo se uma exceção for lançada por um bloco de código no meio do bloco.

Try … finalmente é expresso usando a seguinte sintaxe:

try
   expression1
finally
   expression2

Quando ocorre um erro ou condição excepcional, a função aumentar é usada para notificar o usuário. Como parte do objeto de exceção, as informações sobre o erro também são capturadas.

A sintaxe da função raise pode ser resumida da seguinte forma:

raise (expression)

As exceções F# são geradas pela função failwith.

Sua sintaxe é a seguinte:

failwith error-message-string

Uma exceção de argumento é gerada pela função invalidArg.

invalidArg parameter-name error-message-string

Dividir por exceção de zero

Example: 

1
2
3
4
5
6
7
8
9
10
11
12
This program shows how to handle exceptions with a simple trywith block:
let division a b =
try
Some (a / b)
with
| 😕 System.DivideByZeroException -> printfn "Division by zero!"; None
let result = division 220 0
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Ao compilar o programa, ele imprime o seguinte:

FABLE: Cannot type test (evals to false): System.DivideByZeroException

Erro de tipo

As exceções podem ser declaradas usando o tipo de exceção do F#. Os tipos de exceção podem ser usados ​​diretamente em filtros em uma expressão try…with.
Aqui está um exemplo:

Example: 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
exception Err1 of string
// Using a tuple type as the argument type.
exception Err2 of string * int
let compare a b =
try
if a = b then raise (Err1("Equal Number Error"))
else raise (Err2("Error Not detected", 100))
with
| Err1(str) -> printfn "Error1 %s" str
| Err2(str, i) -> printfn "Error2 %s %d" str i
compare 7 7
compare 22 14
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

E a saída será a seguinte:

Error1 Equal Number Error
Error2 Error Not detected 22

Tratamento de exceções aninhadas

Este exemplo ilustra como lidar com exceções aninhadas da seguinte maneira:

Example: 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
let divideAndLog x y =
try
// Outer try block
let result = x / y
printfn "Result: %d" result
try
// Inner try block
if result = 0 then
raise (System.DivideByZeroException())
else
printfn "No exception in inner try block"
with
| ex ->
printfn "Inner exception: %s" ex.Message
raise ex // re-raise the exception
with
| ex ->
printfn "Outer exception: %s" ex.Message
divideAndLog 10 2
divideAndLog 10 0
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Imprime a seguinte saída:

Result: 5
No exception in inner try block
Outer exception: Attempted to divide by zero.

Falha com função

Abaixo está um exemplo que cumprimenta o usuário se o nome corresponder, caso contrário, ele lançará uma exceção:

Example: 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let greetPerson name =
match name with
| "John" -> printfn "Hello, John! How are you?"
| "Ben" -> printfn "Hello, Ben! How are you?"
| _ -> failwith "Unknown person"
try
greetPerson "Alice"
with
| ex ->
printfn "Caught exception: %s" ex.Message
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

E a saída será:

Caught exception: Unknown person

Exceção invalidArg

InvalidArg gera uma exceção quando encontra um argumento inválido. Isso pode ser demonstrado pelo seguinte programa:

Example: 

1
2
3
4
5
6
7
8
9
10
11
let days = [| "Sunday"; "Monday"; "Tuesday"; "Wednesday"; "Thursday"; "Friday"; "Saturday" |]
let findDay day =
if (day > 7 || day < 1)
then invalidArg "day" (sprintf "You have entered %d." day)
days.[day1]
printfn "%s" (findDay 3)
printfn "%s" (findDay 6)
printfn "%s" (findDay 8)
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

E o resultado será:

Tuesday
Friday

Unhandled Exception:
System.ArgumentException: You have entered 8.
Parameter name: day
  at Main.findDay (System.Int32 day) [0x00028] in <643d27b54fc9e525a7450383b5273d64>:0 
  at <StartupCode$main>.$Main.main@ () [0x000d1] in <643d27b54fc9e525a7450383b5273d64>:0 
[ERROR] FATAL UNHANDLED EXCEPTION: System.ArgumentException: You have entered 8.
Parameter name: day
  at Main.findDay (System.Int32 day) [0x00028] in <643d27b54fc9e525a7450383b5273d64>:0 
  at <StartupCode$main>.$Main.main@ () [0x000d1] in <643d27b54fc9e525a7450383b5273d64>:0

Exemplo de Explicação:

Temos uma lista de dias chamada days com elementos que representam os nomes dos dias da semana. Também temos uma função chamada findDay que recebe um argumento inteiro day , que representa o dia da semana (1 para domingo, 2 para segunda-feira e assim por diante).

Dentro da função findDay, primeiro verificamos se o valor do dia é maior que 7 ou menor que 1 usando uma instrução if . Se for, usamos a função invalidArg para gerar uma exceção com a mensagem de erro “Você digitou x”. onde x é o valor do dia formatado como uma string usando a função sprintf .

Caso contrário, se day for um valor válido entre 1 e 7, usaremos a matriz days para procurar o nome do dia correspondente usando a sintaxe de indexação days.[day – 1] , onde day – 1 é usado para converter o valor do dia para o índice baseado em zero do array .

Por fim, imprimimos o nome do dia retornado pela função findDay para três valores de dia diferentes (3, 6 e 8) usando instruções printfn.

Observe que a função findDay pode gerar uma exceção se um valor de dia inválido for passado, e essa exceção pode ser capturada e tratada usando técnicas apropriadas de tratamento de erros, como um bloco try-with .

Você pode ajudar outras pessoas a aprender sobre o poder e a natureza flexível do tratamento de exceções do F# compartilhando nosso artigo nas mídias sociais abaixo. Isso permitirá que eles criem aplicativos dinâmicos e interativos.


Práticas recomendadas de exceção do F#

Para escrever um código robusto em F#, é importante seguir as práticas recomendadas para manipulação de exceção.

Aqui estão algumas diretrizes a serem lembradas ao lidar com exceções em seu código F#:

  • Geralmente não é recomendado capturar exceções genéricas, como System.Exception, pois pode dificultar a identificação e correção de problemas em seu código. Em vez disso, capture exceções específicas que você espera que sejam geradas e trate-as de acordo. Isso permite que você forneça mensagens de erro significativas e tome as ações apropriadas com base na exceção específica que ocorreu.
  • O código de tratamento de exceção deve ser conciso e focado no tratamento da exceção em questão. Evite adicionar lógica desnecessária ou operações complexas no código de tratamento de exceção, pois isso pode dificultar a compreensão e a manutenção. Mantenha o código no bloco with mínimo e concentre-se em lidar com a exceção e fornecer feedback apropriado ao usuário.
  • Engolir exceções, o que significa capturar uma exceção e não realizar nenhuma ação, pode ocultar possíveis problemas em seu código e dificultar o diagnóstico e a correção de problemas. Em vez de simplesmente ignorar as exceções, considere registrá-las ou exibir mensagens de erro apropriadas para alertar os usuários sobre o problema.
  • O F# fornece recursos avançados de correspondência de padrão que podem ser usados ​​para lidar com diferentes tipos de exceções de maneira diferente. Você pode usar correspondência de padrão para corresponder a tipos de exceção, propriedades ou dados personalizados associados à exceção. Isso permite que você forneça tratamento de erro personalizado com base na exceção específica que ocorreu.
  • Em alguns casos, usar tipos de resultado, como Result <'T, 'TError> , pode ser uma maneira mais funcional de lidar com erros no código F#. Os tipos de resultado permitem que você represente explicitamente casos de sucesso e falha nos valores de retorno das funções, sem usar exceções. Isso pode levar a um código mais previsível e fácil de entender, especialmente em paradigmas de programação funcional.

Conclusão

A manipulação de exceção F# é um aspecto crítico da escrita de código robusto e confiável. Seguindo as melhores práticas para tratamento de exceções, como ser específico na captura de exceções, manter o código de tratamento de exceções focado, evitar engolir exceções, usar correspondência de padrões, considerar tipos de resultados, lidar com exceções em níveis apropriados de abstração e usar registro e monitoramento para rastreamento de erros , você pode efetivamente lidar com exceções e criar aplicativos mais resilientes e estáveis. Lembre-se de testar exaustivamente seu código de tratamento de exceção para garantir sua exatidão. Com o tratamento cuidadoso de exceções, você pode melhorar a estabilidade e a confiabilidade de seu código F#, proporcionando uma melhor experiência para seus usuários e tornando seus aplicativos mais robustos.

Nós valorizamos o seu feedback.
+1
0
+1
0
+1
0
+1
0
+1
0
+1
0
+1
0

Assine a nossa newsletter
Digite seu e-mail para receber um resumo semanal de nossos melhores posts. Saber mais!
ícone