如何通过专用函数强制创建可区分联合值? [英] How can I enforce the creation of a Discriminated Union value through a dedicated function?

查看:15
本文介绍了如何通过专用函数强制创建可区分联合值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何通过专用函数强制创建可区分联合值?

How can I enforce the creation of a Discriminated Union value through a dedicated function?

意图:

我想依靠创建模式来生成只有有效数据的结构.

I want to rely on Creational Patterns to produce structures having valid data only.

因此,我相信我需要通过将其设为只读来限制 DU 值的使用.但是,我不知道如何做到这一点.

Therefore, I believe that I will need to restrict the use of a DU value by making it read-only. However, it's not obvious to me how to accomplish that.

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

我尝试了以下方法:

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

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

However, setting the DU type as private breaks the ability to perform pattern matching on the result of the creation function.

推荐答案

这就是我立刻想到的.

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

You could use an active pattern to determine the cases you want to expose as an API to the outside world and then keep the internal representation of the DU completely private.

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

This would force you to use the publically exposed API to create the discriminated union but still allow pattern matching against the result - something like this:

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

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

Note that if you use the same pattern names, you may have to add the rather nasty type annotations shown above - these would be required to prevent a compiler error if the File2 module were not present - this could be relevant if you are exposing an API in a library but not making use of it. If you use different pattern names, that's obviously not an issue.

这篇关于如何通过专用函数强制创建可区分联合值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆