首先,简短明了的回答:如果您正在构建单体应用程序,则建议为整个应用程序创建仅一个错误类型。
稍后会有更为详细的回答。
type AllErrors =
| FileNotFound of string
| InvalidJsonStructure of string
| OtherErrors ...
这将为您提供一个良好的地方,其中定义了所有错误,并且您可以创建一个统一的
printError
和其他错误处理函数。
有时这是不可能的,例如如果您的代码是模块化的,并且每个模块都有自己的ErrorType,则有两个选项,仍然创建一个唯一类型并映射到它,或者创建一个嵌套的、组合的类型,就像您所做的那样。这是您的决定。在两种情况下,您使用
Result.mapError
从语法上讲,有很多方法可以实现这一点。为了避免嵌套的
match
,您可以使用
Result.bind
和
Result.mapError
let readJson (path : string) : Result<Json, ReadJsonError> =
readFile path
|> Result.mapError ReadFileError
|> Result.bind (fun content ->
parseJson content
|> Result.mapError JsonParseError
)
如果您有一个
result
计算表达式:
type Builder() =
member inline this.Return x = Ok x
member inline this.ReturnFrom x = (x:Result<_,_>)
member this.Bind (w , r ) = Result.bind r w
member inline this.Zero () = Ok ()
let result = Builder()
如果这样的话,它会像这样:
let readJson (path : string) : Result<Json, ReadJsonError> = result {
let! content = readFile path |> Result.mapError ReadFileError
return! parseJson content |> Result.mapError JsonParseError
}
使用运算符:
let (>>= ) vr f = Result.bind f vr
let (|>>.) vr f = Result.mapError f vr
可能是这样:
let readJson (path : string) : Result<Json, ReadJsonError> =
readFile path |>>. ReadFileError
>>= fun content ->
parseJson content |>>. JsonParseError
或者是这个:
let readJson (path : string) : Result<Json, ReadJsonError> =
path
|> readFile
|>>. ReadFileError
>>= fun content ->
content
|> parseJson
|>>. JsonParseError
甚至是这个:
let readJson (path : string) : Result<Json, ReadJsonError> =
path |>
readFile |>>.
ReadFileError >>=
fun content ->
content |>
parseJson |>>.
JsonParseError
好的,这最后一个只是为了好玩。我不建议您像这样编写代码。
另外,您可以简单地创建函数的统一版本:
let readFileU = readFile >> Result.mapError ReadFileError
let readJsonU = parseJson >> Result.mapError JsonParseError
并使用Kleisli运算符将它们绑定在一起:
let (>=>) f g p = f p |> Result.bind g
let readJson = readFileU >=> readJsonU