Обработка ошибок и управление ресурсами

Result

Последнее обновление: 07.08.2024

Для обработки ошибок, которые мы можем легко идентифицировать и проверить их наличие язык F# предоставляет дискриминированное объединение Result. Это объединение является обобщенным и имеет два варианта:

  • Ok

  • Error

Вариант Ok хранит результат в случае успешного выполнения операции, а вариант Error - информацию об ошибке (в случае ее возникновения). Тип Result типизируется двумя параметрами типа - один передставляет тип данных, который передается в Ok, а второй представляет тип информации об ошибке, который передается в вариант Result.

Например, возьмем простейшую функцию:

let divide a b = a / b 

Функция divide делит два числа и возвращает результат операции. Однако делитель может представлять 0, в этом случае программа завершится с ошибков. Эту ситуацию мы можем легко проверить в функции. И поэтому это тот случай, когда можно использовать тип Result. Так, определим следующую функцию:

let divide a b = 
    if b = 0 then Error "На ноль делить нельзя"
    else Ok (a / b) 

Если делитель представляет число 0, тогда мы возвращаем вариант Error с текстом ошибки. Если делитель ненулевой, тогда возвращаем вариант Ok, в который передается результат операции.

Теперь применим эту функцию в программе:

let divide a b = 
    if b = 0 then Error "На ноль делить нельзя"
    else Ok (a / b) 

let print_result result = 
    match result with
    | Ok n -> printfn "result = %d" n
    | Error msg -> printfn "Error: %s" msg

print_result (divide 10 2)  // result = 5
print_result (divide 10 0)  // Error: На ноль делить нельзя

Для вывода результата на консоль определена вспомогальная функция print_result, которая выводит определенное сообщение га основании результата.

Другой пример:

type Person = { name: string; age:int;}

let createPerson name age = 
    if name = "" then Error "Name is empty"
    elif age > 110 || age < 0 then Error "Age is invalid"
    else Ok({name= name; age = age})

let printResult result = 
    match result with   
    | Error msg -> printfn "%s" msg 
    | Ok person -> printfn "Person witn name `%s` and age %d" person.name person.age

let tom = createPerson "Tom" 40 
let bob = createPerson "Bob" 100500
let sam = createPerson "" 29

printResult tom     // Person witn name `Tom` and age 40
printResult bob     // Age is invalid
printResult sam     // Name is empty

Здесь определена функция createPerson, которая создает объект Person. Однако при создании в функцию могут быть переданы некорректные данные. В этом случае возвращаем вариант Error с соответствующим сообщением об ошибке. Иначе возвращаем вариант Ok, в который передается созданный объект Person.

Нередко для ошибок определяют кастомный тип, который передает информацию об ошибке:

type Person = { name: string; age:int;}

// тип ошибок
type PersonError =
| AgeError of int   // с ошибкой передаем некорректный возраст
| NameError

let createPerson name age = 
    if name = "" then Error NameError
    elif age > 110 || age < 0 then Error (AgeError age)
    else Ok({name= name; age = age})

let printResult result = 
    match result with   
    | Error NameError -> printfn "Name is empty"
    | Error (AgeError age) -> printfn "Age %d is invalid"  age
    | Ok person -> printfn "Person witn name `%s` and age %d" person.name person.age

let tom = createPerson "Tom" 22 
let bob = createPerson "Bob" 100500
let sam = createPerson "" 29

printResult tom
printResult bob 
printResult sam
Помощь сайту
Юмани:
410011174743222
Номер карты:
4048415020898850