我喜欢简单的类型
type Code = Code of string
但我想对字符串设置一些限制(在这种情况下 - 不允许空格仅限字符串).就像是
type nonemptystring = ??? type Code = Code of nonemptystring
如何用F#惯用法定义这种类型?我知道我可以使用构造函数或带有工厂函数的受限模块使其成为一个类,但是有一个简单的方法吗?
A string
本质上是一系列char
值(在Haskell中,BTW,String
是一个类型别名[Char]
).那么,更普遍的问题是,是否可以静态地将列表声明为具有给定大小.
这种语言特征被称为依赖类型,而F#没有它.因此,简短的回答是,这不可能以声明的方式进行.
那么,最简单的,也可能是最惯用的方式是定义Code
为单一案例的歧视联盟:
type Code = Code of string
在定义的模块中Code
,您还可以定义客户端可用于创建Code
值的函数:
let tryCreateCode candidate = if System.String.IsNullOrWhiteSpace candidate then None else Some (Code candidate)
此函数包含阻止客户端创建空Code
值的运行时逻辑:
> tryCreateCode "foo";; val it : Code option = Some (Code "foo") > tryCreateCode "";; val it : Code option = None > tryCreateCode " ";; val it : Code option = None
什么阻止客户端创建无效Code
值呢?例如,客户端是否能够绕过该tryCreateCode
功能并简单地写Code ""
?
这是签名文件的来源.您创建一个签名文件(.fsi
),并在其中声明类型和函数,如下所示:
type Code val tryCreateCode : string -> Code option
这里Code
声明了类型,但它的'构造函数'不是.这意味着您无法直接创建此类型的值.例如,这不编译:
Code ""
给出的错误是:
错误FS0039:未定义值,构造函数,命名空间或类型"代码"
创建Code
值的唯一方法是使用该tryCreateCode
函数.
如此处所示,您不能再访问基础字符串值Code
,除非您还提供了以下函数:
let toString (Code x) = x
并在与.fsi
上面相同的文件中声明它:
val toString : Code -> string
这可能看起来像很多工作,但实际上只有六行代码和三行类型声明(在.fsi
文件中).