我喜欢像
这样的简单类型type Code = Code of string
但是我想对字符串设置一些限制(在这种情况下 - 不允许空出仅限空格的字符串)。像
这样的东西type nonemptystring = ???
type Code = Code of nonemptystring
如何以F#惯用方式定义此类型?我知道我可以使用构造函数或带有工厂函数的受限模块使其成为类,但有一种简单的方法吗?
答案 0 :(得分:5)
string
本质上是一系列char
值(在Haskell中,BTW,String
是 [Char]
的类型别名。那么,一个更普遍的问题是,如果可以将列表静态声明为具有给定大小,那么就是可能的。
这种语言功能称为Dependent Types,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 ""
?
这是signature files进来的地方。你创建了一个签名文件(.fsi
),并在其中声明了类似的函数:
type Code
val tryCreateCode : string -> Code option
此处声明Code
类型,但其构造函数' ISN'吨。这意味着您无法直接创建此类型的值。例如,这不会编译:
Code ""
给出的错误是:
错误FS0039:值,构造函数,命名空间或类型'代码'未定义
创建Code
值的唯一方法是使用tryCreateCode
函数。
如此处所示,您不能再访问基础字符串值Code
,除非您还为此提供了一个函数:
let toString (Code x) = x
并在与上面相同的.fsi
文件中声明它:
val toString : Code -> string
这可能看起来很多,但实际上只有六行代码和三行类型声明(在.fsi
文件中)。
答案 1 :(得分:0)
不幸的是,没有方便的语法来声明类型的受限子集,但我会利用活动模式来执行此操作。正如您所说,您可以在构建类型时进行检查并检查其有效性:
/// String type which can't be null or whitespace
type FullString (string) =
let string =
match (System.String.IsNullOrWhiteSpace string) with
|true -> invalidArg "string" "string cannot be null or whitespace"
|false -> string
member this.String = string
现在,天真地构造这个类型可能会抛出运行时异常,我们不希望这样!所以让我们使用活动模式:
let (|FullStr|WhitespaceStr|NullStr|) (str : string) =
match str with
|null -> NullStr
|str when System.String.IsNullOrWhiteSpace str -> WhitespaceStr
|str -> FullStr(FullString(str))
现在我们可以使用模式匹配语法来构建我们的FullString
。这个函数在运行时是安全的,因为如果我们处于有效的情况下,我们只创建一个FullString
。
你可以像这样使用它:
let printString str =
match str with
|NullStr -> printfn "The string is null"
|WhitespaceStr -> printfn "The string is whitespace"
|FullStr fstr -> printfn "The string is %s" (fstr.String)