键入表示非空的字符串或F#中的空格

时间:2015-12-10 22:25:07

标签: f#

我喜欢像

这样的简单类型
type Code = Code of string

但是我想对字符串设置一些限制(在这种情况下 - 不允许空出仅限空格的字符串)。像

这样的东西
type nonemptystring = ???
type Code = Code of nonemptystring

如何以F#惯用方式定义此类型?我知道我可以使用构造函数或带有工厂函数的受限模块使其成为类,但有一种简单的方法吗?

2 个答案:

答案 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)