处理F#中的多个模式匹配错误。

huangapple go评论41阅读模式
英文:

Deal with multiple errors in Pattern Matching F#

问题

我试图处理在F#中在同一个匹配语句中处理多种Result类型的情况,更准确地说,我正在组合如果出现错误时应该返回的内容。

当我只处理一个或两个Result类型时,为每种可能的结果编写代码很容易,但随着每个额外的项目,情况变得越来越复杂。有没有更好的方法在同一时间处理多个Result类型?

我试图避免使用“_”通配符,因为这可能会导致在添加或更改代码时出现错误。

这是一个例子:

let validateAppointmentAction =
    match (getAppointmentConfirmation, getAppointmentResult, getDossierResult, getInvoiceResult) with
    | (Ok appointmentConfirmation, Ok appointment, Ok dossier, Ok invoiceOption) ->
        // 在这种情况下返回一个AppointmentValidationAction的逻辑
    | (Error e1, Ok appointment, Ok dossier, Ok (invoiceOption)) -> AppointmentValidationAction.Err e1.Message
    | (Ok appointmentConfirmation, Error e2, Ok dossier, Ok (invoiceOption)) -> AppointmentValidationAction.Err e2.Message
    | (Error e1, Error e2, Ok dossier, Ok (invoiceOption)) -> AppointmentValidationAction.Err $"{e1.Message} 和 {e2.Message}"
    ...
英文:

I'm trying to deal with multiple Result types in the same match...with statement in F#, being more precise, I'm composing what should be returned if anything is an Error.

When I'm dealing with just one or two Result types it's easy to write the code for each possible outcome, but with each additional item, it starts to get more and more complex. Is there a better way to work with multiple Result types at the same time?

I try to avoid using the "_" wildcard since that could lead into errors crawling into the logic when adding or changing something in the code.

Here's an example:

let validateAppointmentAction =
    match (getAppointmentConfirmation, getAppointmentResult, getDossierResult, getInvoiceResult) with
    | (Ok appointmentConfirmation, Ok appointment, Ok dossier, Ok invoiceOption) ->
        // Some logic that returns AppointmentValidationAction in this case
    | (Error e1, Ok appointment, Ok dossier, Ok (invoiceOption)) -> AppointmentValidationAction.Err e1.Message
    | (Ok appointmentConfirmation, Error e2, Ok dossier, Ok (invoiceOption)) -> AppointmentValidationAction.Err e2.Message
    | (Error e1, Error e2, Ok dossier, Ok (invoiceOption)) -> AppointmentValidationAction.Err $"{e1.Message} and {e2.Message}"
    ...

答案1

得分: 1

以下是翻译好的部分:

一种解决方法是声明一个错误类型,它包装了可能预期到的所有不同类型的错误,然后使用 FsToolkit.ErrorHandling 库编写一个函数,该函数在一切正常时返回结果,或者返回错误列表:

open FsToolkit.ErrorHandling

module ValidateEverything =
    type AppointmentConfirmation = AppointmentConfirmation of string
    type Dossier = Dossier of string
    
    type AppointmentConfirmationE =
        | TooLate
        | TooEarly
       
    type DossierE =
        | DossierNotFound
        | DossierNotValidated
    
    type CommonE =
        | AppointmentConfirmationError of AppointmentConfirmationE
        | DossierError of DossierE
    
    let validateAppointmentAction
        (appointmentConfirmationR : Result<AppointmentConfirmation, AppointmentConfirmationE>)
        (dossierR : Result<Dossier, DossierE>)
        = validation {
           
           let! appointmentConfirmation =
              appointmentConfirmationR
              |> Result.mapError CommonE.AppointmentConfirmationError
           
           and! dossier =
              dossierR
              |> Result.mapError CommonE.DossierError
           
           return appointmentConfirmation, dossier
        }
         

此函数返回一个:

Validation<(AppointmentConfirmation * Dossier),CommonE>

与以下相同:

Result<(AppointmentConfirmation * Dossier),List<CommonE>>

在 dossier 前面的 and! 导致 dossierR 被计算(并添加到列表中),即使第一个 let! 导致返回错误也是如此。

mapError 语句将各种错误映射到一个联合类型中,以便它们可以具有相同的类型,因为在 F# 中,列表必须只包含一个类型的值。

英文:

One way of solving this is to declare an error type that wraps all the different errors that you may expect, and then use the FsToolkit.ErrorHandling library to write a function that either returns the result when everything went well, or a list of errors:

open FsToolkit.ErrorHandling

module ValidateEverything =
    type AppointmentConfirmation = AppointmentConfirmation of string
    type Dossier = Dossier of string
    
    type AppointmentConfirmationE =
        | TooLate
        | TooEarly
       
    type DossierE =
        | DossierNotFound
        | DossierNotValidated
    
    type CommonE =
        | AppointmentConfirmationError of AppointmentConfirmationE
        | DossierError of DossierE
    
    let validateAppointmentAction
        (appointmentConfirmationR : Result&lt;AppointmentConfirmation, AppointmentConfirmationE&gt;)
        (dossierR : Result&lt;Dossier, DossierE&gt;)
        = validation {
           
           let! appointmentConfirmation =
              appointmentConfirmationR
              |&gt; Result.mapError CommonE.AppointmentConfirmationError
           
           and! dossier =
              dossierR
              |&gt; Result.mapError CommonE.DossierError
           
           return appointmentConfirmation, dossier
        }
         

This function returns a:

Validation&lt;(AppointmentConfirmation * Dossier),CommonE&gt;

which is the same as

Result&lt;(AppointmentConfirmation * Dossier),List&lt;CommonE&gt;&gt;.

The and! in front of dossier causes dossierR to be calculated (and added to the list) even if the first let! results in an error being returned.

The mapError statements map the various errors into a union, so that they can have the same type, since in F# lists have to contain values of only one type.

huangapple
  • 本文由 发表于 2023年5月28日 13:39:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/76350107.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定