Для обработки ошибок, которые мы можем легко идентифицировать и проверить их наличие язык 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