如何解决F#自定义Validation类型的嵌套包装问题
解决方案
你的问题本质是当前自定义的Validation<'a>类型只实现了应用函子的能力,缺少单子的扁平化能力,你遇到的嵌套包装问题是这类 elevated type 非常常见的场景,不需要用嵌套match,有两种通用的优雅解法:
方案1:基于基础函数组合实现
首先实现标准的bind函数,这是处理返回 elevated type 函数的核心工具:
let bind (f: 'a -> Validation<'b>) (validation: Validation<'a>) : Validation<'b> = match validation with | Success value -> f value | Failure errors -> Failure errors
你原来用apply的写法只需要加一步bind id就能消除嵌套:
let formatReport (unvalidatedLanguageName: string) (unvalidatedReport: UnvalidatedReport): Validation<string> = Success formatReportAsText <*> languageTranslatorFor unvalidatedLanguageName <*> reportFrom unvalidatedReport |> bind id
这里的bind id和你自己写的merge函数逻辑完全等价,是这类嵌套场景的标准处理方式。
方案2:基于计算表达式实现(更符合F#惯用写法)
你已经找到的ReturnFrom + return!的方案是完全正确的,也是行业通用的标准写法,不存在笨拙的问题。
如果需要支持短路校验(第一个参数校验失败后直接返回错误,不继续校验第二个参数),你还可以给计算表达式补充Bind方法:
type MergeValidationBuilder() = member _.BindReturn(validation: Validation<'a>, func) = Validation.map validation func member _.MergeSources(validation1, validation2) = Validation.zip validation1 validation2 member _.ReturnFrom(validation) = validation // 新增Bind方法支持短路校验的let!链式写法 member _.Bind(validation, f) = bind f validation let validate = MergeValidationBuilder()
之后你就可以根据需求选择写法:
- 用
and!:并行校验所有参数,累积所有错误返回 - 用串行
let!:校验失败立即短路,只返回前面的错误
内容的提问来源于stack exchange,提问作者Bremewoord
相关产品推荐
相关产品推荐

