如何通过专用功能强制创建Discriminated Union值?

Sco*_*rod 10 f#

如何通过专用功能强制创建Discriminated Union值?

意图:

我想依靠Creational Patterns来生成只有有效数据的结构.

因此,我认为我需要通过将其设为只读来限制DU值的使用.但是,对我来说,如何实现这一目标并不明显.

module File1 =

    type EmailAddress = 
        | Valid   of string 
        | Invalid of string

    let createEmailAddress (address:System.String) =
        if address.Length > 0
        then Valid    address 
        else Invalid  address

module File2 =

    open File1

    let validEmail = Valid "" // Shouldn't be allowed

    let isValid = createEmailAddress ""

    let result = match isValid with
                 | Valid x -> true
                 | _       -> false
Run Code Online (Sandbox Code Playgroud)

我尝试了以下方法:

type EmailAddress =
    private
    | Valid   of string 
    | Invalid of string
Run Code Online (Sandbox Code Playgroud)

但是,将DU类型设置为私有会破坏对创建函数的结果执行模式匹配的能力.

The*_*ght 11

这就是我们想到的东西.

您可以使用活动模式来确定要作为API向外部世界公开的案例,然后将DU的内部表示完全保密.

这会强制您使用公开公开的API来创建区分联合,但仍允许对结果进行模式匹配 - 如下所示:

module File1 =

    type EmailAddress = 
        private
        | Valid   of string 
        | Invalid of string

    let createEmailAddress (address:System.String) =
        if address.Length > 0
        then Valid    address 
        else Invalid  address

    // Exposed patterns go here
    let (|Valid|Invalid|) (input : EmailAddress) : Choice<string, string>  = 
        match input with
        | Valid str -> Valid str
        | Invalid str -> Invalid str

module File2 =

    open File1

    let validEmail = Valid "" // Compiler error

    let isValid = createEmailAddress "" // works

    let result = // also works
        match isValid with
        | Valid x -> true
        | _       -> false
Run Code Online (Sandbox Code Playgroud)

请注意,如果使用相同的模式名称,则可能必须添加上面显示的相当讨厌的类型注释 - 如果File2模块不存在,则需要这些注释以防止编译器错误- 如果您在API中公开API,这可能是相关的图书馆但没有使用它.如果您使用不同的模式名称,那显然不是问题.

  • 哇!我第一次看到活动模式的使用;-) (2认同)