Обработка ошибок

def try_read_file (path : String) : Result<String, FileError> = ...

object i32
    def try_parse (s : String) : Option<i32> = ...

Result<String, FileError> содержит либо прочитанный из файла текст в Ok<String>, либо Error<FileError> с сообщением об ошибке.

def try_add (lhs_path : String) (rhs : i32) =
    let lhs =
        let! text = try_read_file lhs_path
        let! number = i32.try_parse text
        number

    lhs + rhs |> Some

Привязки text и number имеют типы String и i32, учитывающие только случаи успешного выполнения функций try_read_file и try_parse.

Ниже показан аналогичный код без let!.

def try_add (lhs_path : String) (rhs : i32) =
    let lhs =
        let text = case try_read_file lhs_path of
            Result/Ok text -> text
            Result/Error _ -> return None

        let number = case i32.try_parse text of
            Some number -> number
            None -> return None

        number

    lhs + rhs |> Some

Полученное от try_read_file сообщение об ошибке можно передать выше по стеку вызовов, вернув Result<i32, Error> из try_add вместо Option<i32>.

type ParseError = String

type Error =
    | t@ FileError
    | t@ ParseError

def try_add (lhs_path : String) (rhs : i32) =
    let lhs =
        let! text = try_read_file lhs_path
        let! number = let error = ParseError text
                      i32.try_parse text |> to_result error
        number

    lhs + rhs |> Result/Ok

Option конвертируется в Result с помощью метода to_result.