在Ada子类型等函数式语言中定义自然数

时间:2013-09-14 10:10:35

标签: functional-programming subtype

在Ada中定义自然数,你可以这样写:

subtype Natural  is Integer range 0 .. Integer'Last;

这是类型安全的,并在编译时进行检查。它很简单(一行代码)和高效(它不像许多函数式语言那样使用递归来定义自然数)。是否有任何功能语言可以提供类似的功能?

由于

2 个答案:

答案 0 :(得分:3)

  

这是类型安全的,并在编译时进行检查。

正如您在问题的评论中已经指出的那样,在编译时不会检查它。 Modula-2中的等效功能或任何其他可用于生产的通用编程语言都不具备。

在编译时检查这样的约束的能力需要依赖类型,细化类型或类似结构。您可以在CoqAgda等定理证明器中找到这些类型的功能,或者在ATSLiquid Haskell等实验/学术语言中找到这些功能。

现在,我提到Coq和Agda这些语言递归地定义了他们的Nat类型,所以这不是你想要的,而ATS是一种命令式语言。因此离开Liquid Haskell(当然还有我没有提及的语言)。 Liquid Haskell是带有额外类型注释的Haskell,因此它绝对是一种功能语言。在Liquid Haskell中,您可以定义MyNat(已在标准库中定义的名为Nat的类型)类型:

{-@ type MyNat = {n:Integer | n >= 0} @-}

然后像这样使用它:

{-@ fac :: MyNat -> MyNat @-}
fac :: Integer -> Integer
fac 0 = 1
fac n = n * fac (n-1)

如果您尝试使用负数作为参数调用fac,则会出现编译错误。如果使用用户输入作为参数调用它,您将也会收到编译错误,除非您专门检查输入是非负的。如果您删除了fac 0 = 1行,也会收到编译错误,因为下一行的n现在可能为0,当您致电n-1fac (n-1)为负,所以编译器会拒绝它。

应该注意的是,即使使用最先进的类型推理技术,像这样的语言中的非平凡程序最终会有相当复杂的类型签名,您将花费大量时间和精力追逐类型错误通过一个日益复杂的类型签名丛林,只有难以理解的类型错误来指导你。因此,这些功能为您提供安全的价格。还应该指出的是,在图灵完整语言中,您偶尔必须为您知道不可能发生的情况编写运行时检查,因为即使您认为应该编译器也无法证明所有内容。

答案 1 :(得分:1)

Typed Racket,一种Racket的类型方言,有一组丰富的数字子类型,它知道大量的闭包属性(例如,两个非负数的总和是非负的,两个精确的总和整数是一个精确的整数,等等。这是一个简单的例子:

#lang typed/racket
(: f : (Nonnegative-Integer Nonnegative-Integer -> Positive-Integer))
(define (f x y)
  (+ x y 1))

类型检查是静态完成的,但当然,类型检查器无法证明关于数字子类型的每个真实事实。例如,以下函数实际上只返回类型Nonnegative-Integer的值,但减法的类型规则只允许TR结束Integer的结果类型。

> (lambda: ([x : Nonnegative-Integer] [y : Nonnegative-Integer])
    (- x (- x y)))
- : (Nonnegative-Integer Nonnegative-Integer -> Integer)
#<procedure>

数字球拍的数字方法在St-Amour等人的键入数字塔中有所描述(见于PADL 2012)。通常会有一篇指向论文here的链接,但此时此链接似乎已被打破。如果您搜索标题,Google会将PDF缓存呈现为HTML。