Design patterns 什么';这是一个具有受保护的创作和公共读访问的歧视性联盟的功能设计模式吗?

Design patterns 什么';这是一个具有受保护的创作和公共读访问的歧视性联盟的功能设计模式吗?,design-patterns,f#,abstract-data-type,discriminated-union,Design Patterns,F#,Abstract Data Type,Discriminated Union,歧视联合通常被用作数据持有者,并提供有关其持有内容的信息,但有时我发现自己需要防止创建歧视联合,但仍然能够使用熟悉的语法对其进行模式匹配 为了便于讨论,假设我们用一个字符串表示一个URI,但我想创建一个具有保证的已验证URI(即,它在RFC中有效)的类型,它也是一个字符串。仅使用Some/None在这里不起作用,因为我仍然希望访问任何无效字符串。另外,我喜欢对当前代码库进行温和的重构(在多行代码中用新的单例联合替换现有的单例联合要比使用多例联合容易得多) 我可以按如下方式解决这个问题,我认为这说

歧视联合通常被用作数据持有者,并提供有关其持有内容的信息,但有时我发现自己需要防止创建歧视联合,但仍然能够使用熟悉的语法对其进行模式匹配

为了便于讨论,假设我们用一个字符串表示一个URI,但我想创建一个具有保证的已验证URI(即,它在RFC中有效)的类型,它也是一个字符串。仅使用Some/None在这里不起作用,因为我仍然希望访问任何无效字符串。另外,我喜欢对当前代码库进行温和的重构(在多行代码中用新的单例联合替换现有的单例联合要比使用多例联合容易得多)

我可以按如下方式解决这个问题,我认为这说明了我打算做什么(为了简单起见,不考虑错误情况):


[作为一种语言特性。如果您认为它应该在:)中,请投票。

从相当一般的意义上讲,我认为您描述的模式是抽象数据类型-这不是特定F#实现的名称,但它非常适合您的描述

引用巴巴拉·利斯科夫(Barbara Liskov)和斯蒂芬·齐勒斯(Stephen Zilles)1974年的话:

一个 摘要 资料 类型 定义 A. 阶级 属于 摘要 物体 哪个 是 完全 具有特征的 通过 这个 操作 可用 在…上 那些 物体。 这 方法 那个 一 摘要 资料 类型 可以 是 定义 通过 决定性的 这个 刻画 操作 对于 那个 类型

在您的示例中,您正在定义一个抽象数据类型
VerifiedUrl
,该类型由三个操作描述。这些操作是
create
(或
tryCreate
)创建抽象数据类型的a值,操作
get
允许您获取该值。创建该值的操作还捕获一个事实,即您只能从有效的URL字符串创建
VerifiedUrl


这种模式可能更关注这样一个事实,即您隐藏了实现细节,只公开了用于操作它的某些操作——而在您的例子中,另一个重要的事实是抽象数据类型的值满足某些属性——但是您可以将这些视为抽象数据类型的不变量我想不出一个更好的概念来捕捉这个想法。

好吧,简而言之:你不需要隐藏你的选项。你只需要确保有足够的选项。你还可以提供适当的singnatures函数来映射类型

现在,较长的版本:
单一责任制(TM)这里也适用。具体的联合类型必须专门用于具体的问题。在您的情况下,让
类型验证stamp=Verified…| NotYetVerified |您不应传递
选项。并且您不应隐藏它们:这没有很好的理由。然后,您定义
验证
函数以及您希望向代码客户端提供的其他内容。这里是您保持正确的地方:通过将函数绑定到合理的类型;例如,
verify
肯定会使用原始
string
,而不是包装到容器中的字符串;但它会返回
VerificationStamp“myUrl”

有趣的引用!我不知道这就是我正在做的事情,但正如你所描述的,这个概念是有意义的。顺便说一句,你会同意像我展示的那样捕获一个抽象数据类型,或者你更喜欢F#中的另一种方法吗?我正在考虑通过创建一个
验证来抽象掉细节ed
DU,其中“create”和“tryCreate”采用了一个创建内部通用值的函数。这将使其更广泛地适用于具有这些属性(此处为:可验证性)的任何数据类型,您认为这是函数式语言中的一种常见编程模式吗?到目前为止,我的工作基本上没有这一点(使用无处不在的
选项
结果
),但我肯定能在我的代码库中找到几个有用的地方。@Abel,嗯,有一些镜头(看看Haskell的实现),基于范畴论中的Yoneda引理,它是专门为这种“隐藏”而设计的目的..但是..更常见的做法是避免它,并使事情更简单。@Abel-我认为你所拥有的是一个很好的方法,通常是有用的。我怀疑像泛型
Verified这样的操作我对这个概念做得更多了,它提供了我所需要的东西。一个行为类似于DU的不可变类型,但不能是在没有验证的情况下实例化。当然,您会失去一些DU优势,比如编译为查表IL代码,但对于某些特定的用例,它似乎非常实用。再次感谢;)
[<AutoOpen>]
module VerifiedUriModule =
    module VerifiedUri =
        type VerifiedUri = 
            private 
            | VerifiedUri of string

        let create uri = VerifiedUri uri  // validation and error cases go here

        let tryCreate uri = Some <| VerifiedUri uri  // or here

        let get (VerifiedUri uri) = uri

    let (|VerifiedUri|) x =
        VerifiedUri.get x