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

Mik*_*kov 7 f#

我喜欢简单的类型

type Code = Code of string
Run Code Online (Sandbox Code Playgroud)

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

type nonemptystring = ???
type Code = Code of nonemptystring
Run Code Online (Sandbox Code Playgroud)

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

Mar*_*ann 7

A string本质上是一系列char值(在Haskell中,BTW,String 一个类型别名[Char]).那么,更普遍的问题是,是否可以静态地将列表声明为具有给定大小.

这种语言特征被称为依赖类型,而F#没有它.因此,简短的回答是,这不可能以声明的方式进行.

那么,最简​​单的,也可能是最惯用的方式是定义Code为单一案例的歧视联盟:

type Code = Code of string
Run Code Online (Sandbox Code Playgroud)

在定义的模块中Code,您还可以定义客户端可用于创建Code值的函数:

let tryCreateCode candidate =
    if System.String.IsNullOrWhiteSpace candidate
    then None
    else Some (Code candidate)
Run Code Online (Sandbox Code Playgroud)

此函数包含阻止客户端创建空Code值的运行时逻辑:

> tryCreateCode "foo";;
val it : Code option = Some (Code "foo")
> tryCreateCode "";;
val it : Code option = None
> tryCreateCode "   ";;
val it : Code option = None
Run Code Online (Sandbox Code Playgroud)

什么阻止客户端创建无效Code值呢?例如,客户端是否能够绕过该tryCreateCode功能并简单地写Code ""

这是签名文件的来源.您创建一个签名文件(.fsi),并在其中声明类型和函数,如下所示:

type Code
val tryCreateCode : string -> Code option
Run Code Online (Sandbox Code Playgroud)

这里Code声明了类型,但它的'构造函数'不是.这意味着您无法直接创建此类型的值.例如,这不编译:

Code ""
Run Code Online (Sandbox Code Playgroud)

给出的错误是:

错误FS0039:未定义值,构造函数,命名空间或类型"代码"

创建Code值的唯一方法是使用该tryCreateCode函数.

如此处所示,您不能再访问基础字符串值Code,除非您还提供了以下函数:

let toString (Code x) = x
Run Code Online (Sandbox Code Playgroud)

并在与.fsi上面相同的文件中声明它:

val toString : Code -> string
Run Code Online (Sandbox Code Playgroud)

这可能看起来像很多工作,但实际上只有六行代码和三行类型声明(在.fsi文件中).

  • 不会将类型构造函数设为私有吗?`type Code =字符串的私有代码` (2认同)

The*_*ght 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
Run Code Online (Sandbox Code Playgroud)

现在,天真地构造这种类型可能会引发运行时异常,而我们不希望这样!所以让我们使用主动模式:

let (|FullStr|WhitespaceStr|NullStr|) (str : string) =
    match str with
    |null -> NullStr
    |str when System.String.IsNullOrWhiteSpace str -> WhitespaceStr
    |str -> FullStr(FullString(str))
Run Code Online (Sandbox Code Playgroud)

现在我们有了一些可以与模式匹配语法一起使用的东西来构建我们的FullStrings. 这个函数在运行时是安全的,因为我们只有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)
Run Code Online (Sandbox Code Playgroud)