如何创建其值只能在有效范围内的类型?

Sco*_*rod 6 f#

如何创建其值只能在有效范围内的类型?

大多数静态类型语言提供数字类型,其中每种类型都支持一系列值.

一个INT是这样类型的一个例子.

支持以下内容:

使非法国家无法代表

如何创建一个类型,使得超出范围的值赋值在编译时导致错误?

例如:类型PositiveInteger //范围是0到2,147,483,647

UPDATE

我试过这个:

type PositiveInteger = private PInt of int with
    static member FromInt i =
       if i <= 0 then failwith "out of range" else PInt i

let isSuccessful = 
    PInt -1
Run Code Online (Sandbox Code Playgroud)

但是,当我希望它在编译时抛出"超出范围"时,上面的代码仍会编译.可以说编译时不支持这个吗?

Car*_*ten 6

让我们继续这个例子(并将其扩展为正p <=> p> 0)

你可以随时去使用一些数学(这里使用peano公理的略微修改 来定义正数自然数):

type PositiveInteger =
   | One
   | Succ of PositiveInteger
Run Code Online (Sandbox Code Playgroud)

这将有更大的范围

当然这有点难以使用:

let one = One
let two = Succ one
...
let rec add a b =
     match (a,b) with
     | (One, b)    -> Succ b
     | (Succ a, b) -> Succ (add a b)
Run Code Online (Sandbox Code Playgroud)

这通常效率不高(虽然它通常用在类型级别......如果语言支持它)


所以大多数人可能会使用某种智能构造函数的快速失败方法:

type PositiveInteger = private PInt of int with
    static member FromInt i =
       if i <= 0 then failwith "out of range" else PInt i
Run Code Online (Sandbox Code Playgroud)

如果你错过了这个,那么这个也应该编译with:

type PositiveInteger = 
    private | PInt of int
    static member FromInt i =
        if i <= 0 then failwith "out of range" else PInt i
Run Code Online (Sandbox Code Playgroud)

  • 是的,当然*智能构造函数*在运行时发生 - 很难真正将所有内容都下载到编译器中,因为F#对此没有足够的表达能力 - 如果你对这种东西感兴趣那么你应该研究依赖类型 - GOQ,AGDA,IDRIS和越来越多的Haskell用这种方式命名一些支持远远超过F#的知名语言 (4认同)