如何通过专用函数强制创建可区分联合值? [英] How can I enforce the creation of a Discriminated Union value through a dedicated function?
问题描述
如何通过专用函数强制创建可区分联合值?
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屋!