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)
我认为实现您想要做的事情的最佳方法是从具有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)