F# 是否可以识别 DU 的重叠并使用正确的重叠本身?

Ale*_* 75 2 f# discriminated-union

type GenericResult =
    | Ok
    | Error of string

type LoginResult =
    | Ok
    | UserNotFound
    | WrongPassword

let check something:GenericResult =
    match something with
    //| true -> Ok // error:This expression was expected to be of type "GenericREsult" but here has type "LoginResult"
    | true -> GenericResult.Ok // I'm forced to specify GenericResult.Ok
    | false -> Error "aargg!"

let checkLogin something:LoginResult =
    match something with
    | true -> Ok // here I don't need to specify the DU because this is defined after
    | _ -> WrongPassword
Run Code Online (Sandbox Code Playgroud)

我想在这两种方法中只使用“Ok”,而不需要指定 DU。
我发现,如果值发生冲突,最后一个是“预定义的”。

理想情况下,我希望有一种继承
,可以在另一个 DU 中重用一个 DU 的一部分。
例如:

type GenericResult =
    | Ok
    | Error of string

type LoginResult =
    //| GenericResult.Ok
    | UserNotFound
    | WrongPassword

type SaveResult =
    | Created
    | Updated
    //| GenericResult.Error


let checkLogin something: LoginResult | GenericResult.Ok =
    match something with
    | true -> Ok 
    | _ -> WrongPassword
Run Code Online (Sandbox Code Playgroud)

[编辑] 我觉得需要此功能的真实场景是来自 3 个不同逻辑类的 3 个不同结果。
将来会出现更多情况,因此重复 DU 值的倍数将会增加。

// DUs ordered from the most specific to the most generic

type BalanceUpdateResult = 
| Created
| Updated
| InvalidRequest of string

type DeleteResult =
| Ok
| InvalidRequest of string

type Result<'T> =
| Ok of 'T
| NotValid of string
| Error of string
Run Code Online (Sandbox Code Playgroud)

目标是在使用者中拥有干净的匹配语法,例如,DU 的值最终将用于引发异常或返回创建的值。

// balance update function (result is BalanceUpdateResult):
    match result with
    | Created -> this.createOkWithStatus 201 
    | Updated -> this.createOkWithStatus 200
    | InvalidRequest error -> this.createErrorForConflict error

// company creation function (result is Result<Company>):
    match result with 
    | Result.Ok newItem -> 
        context.Logger.Log $"Company created. New Id:{newItem.Id}, Name:{newItem.Name}."
        this.createCreated newItem
    | NotValid error -> base.createErrorForConflict error
    | Error error -> base.createError error

Run Code Online (Sandbox Code Playgroud)

例如,在第二种情况下,InvalidRequest 不会被接受,因为它属于错误的 DU。
必须在各处指定 DU 会导致混乱,如下例所示(请参阅许多Result<_>.):

    interface ICompanyLogic with
        member this.Create(company:Company):Result<Company> =
            match normalize company |> validate with
            | NotValid msg -> Result<_>.NotValid msg
            | Valid validCompany ->
                match companyRepository.Exists(validCompany.Name) with 
                | true -> Result<_>.NotValid($"A company with name \"{validCompany.Name}\" already exists.")
                | _ -> 
                    let newCompany = assignNewId validCompany
                    companyRepository.Create(newCompany)
                    Result<_>.Ok(newCompany)
            
        member this.Update (company:Company):Result<Company> =
            let checkNameExists company = 
                match companyRepository.GetByName company.Name with
                | Some c when c.Id <> company.Id -> NotValid $"A company with name \"{company.Name}\" already exists."
                | _ -> Valid company
         
            match normalize company |> validate with
            | NotValid msg -> Result<_>.NotValid msg
            | Valid c -> match checkNameExists c with
                         | Valid c -> companyRepository.Update c; Result<_>.Ok c
                         | NotValid msg -> Result<_>.NotValid msg
            
Run Code Online (Sandbox Code Playgroud)

Tom*_*cek 6

我认为实现您想要做的事情的最佳方法是从具有Result表示错误类型的类型参数的泛型类型开始:

type Result<'TError> =
    | Ok
    | Error of 'TError
Run Code Online (Sandbox Code Playgroud)

这允许您使用不同的类型来表示错误,包括string,但也可以使用另一个 DU 来捕获更具体的错误类型。然后,您可以将GenericResult和定义LoginResult为两个类型别名:

type LoginError =
    | UserNotFound
    | WrongPassword

type GenericResult = Result<string>
type LoginResult = Result<LoginError>
Run Code Online (Sandbox Code Playgroud)

要报告登录错误,您现在可以将Error WrongPassword特定错误包装在通用Error构造函数中。这两个函数的实现如下所示:

let check something:GenericResult =
  match something with
  | true -> Ok
  | false -> Error "aargg!"

let checkLogin something:LoginResult =
  match something with
  | true -> Ok
  | _ -> Error WrongPassword
Run Code Online (Sandbox Code Playgroud)