如何通过专用功能强制创建“已区分联盟"值? [英] 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.
推荐答案
这就是马上想到的东西.
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屋!