如何通过专用功能强制创建“已区分联盟"值? [英] How can I enforce the creation of a Discriminated Union value through a dedicated function?

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

问题描述

如何通过专用功能强制执行已区分联盟"值的创建?

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.

推荐答案

这就是马上想到的东西.

This is just what springs to mind immediately.

您可以使用活动模式来确定要作为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天全站免登陆