【发布时间】:2019-06-11 20:35:43
【问题描述】:
我有一个 F# 可区分联合,我想将一些“构造函数逻辑”应用于构造联合案例中使用的任何值。假设联合看起来像这样:
type ValidValue =
| ValidInt of int
| ValidString of string
// other cases, etc.
现在,我想对实际传入的值应用一些逻辑,以确保它们有效。为了确保我最终不会处理不是真正有效的ValidValue 实例(尚未使用验证逻辑构造),我将构造函数设为私有并公开一个强制执行我的逻辑的公共函数构建它们。
type ValidValue =
private
| ValidInt of int
| ValidString of string
module ValidValue =
let createInt value =
if value > 0 // Here's some validation logic
then Ok <| ValidInt value
else Error "Integer values must be positive"
let createString value =
if value |> String.length > 0 // More validation logic
then Ok <| ValidString value
else Error "String values must not be empty"
这很有效,让我可以强制执行验证逻辑并确保ValidValue 的每个实例都确实有效。但是,问题是该模块之外的任何人都无法在ValidValue 上进行模式匹配以检查结果,从而限制了可区分联合的有用性。
我希望允许外部用户仍然像任何其他 DU 一样进行模式匹配和使用 ValidValue,但如果它具有私有构造函数,则这是不可能的。我能想到的唯一解决方案是将 DU 中的每个值包装在具有私有构造函数的单例联合类型中,并将实际的 ValidValue 构造函数保留为公共。这会将案例暴露给外部,允许它们进行匹配,但仍然主要阻止外部调用者构造它们,因为实例化每个案例所需的值将具有私有构造函数:
type VInt = private VInt of int
type VString = private VString of string
type ValidValue =
| ValidInt of VInt
| ValidString of VString
module ValidValue =
let createInt value =
if value > 0 // Here's some validation logic
then Ok <| ValidInt (VInt value)
else Error "Integer values must be positive"
let createString value =
if value |> String.length > 0 // More validation logic
then Ok <| ValidString (VString value)
else Error "String values must not be empty"
现在调用者可以匹配ValidValue 的情况,但他们无法读取联合情况下的实际整数和字符串值,因为它们被包装在具有私有构造函数的类型中。这可以通过每种类型的value 函数来解决:
module VInt =
let value (VInt i) = i
module VString =
let value (VString s) = s
不幸的是,现在调用者的负担增加了:
// Example Caller
let result = ValidValue.createInt 3
match result with
| Ok validValue ->
match validValue with
| ValidInt vi ->
let i = vi |> VInt.value // Caller always needs this extra line
printfn "Int: %d" i
| ValidString vs ->
let s = vs |> VString.value // Can't use the value directly
printfn "String: %s" s
| Error error ->
printfn "Invalid: %s" error
有没有更好的方法来强制执行我一开始想要的构造函数逻辑,而不会增加其他地方的负担?
【问题讨论】:
标签: f# pattern-matching discriminated-union