使用 (declare (type ...)) 但仍有 'safe' 功能
Using (declare (type ...)) but still have 'safe' functions
是否可以在函数中使用 (declare (type ...))
声明,同时对函数参数执行类型检查,以便生成更快但仍然安全的代码?
例如,
(defun add (x y)
(declare (type fixnum x y))
(the fixnum x y))
当被称为 (add 1 "a")
时会导致未定义的行为,所以我最好将其修改为
(defun add (x y)
(declare (type fixnum x y))
(check-type x fixnum)
(check-type y fixnum)
(the fixnum x y))
但我担心编译器被允许假定 check-type
总是通过并因此省略检查。
所以我的问题是,上面的例子是否如我预期的那样错误,其次,是否有任何常用的惯用语*用于通过优化代码实现类型安全?
*) 我可以想象,例如,使用优化的 lambda 并在进行类型检查后调用它,但我想知道这是否是最优雅的方式。
您始终可以先检查类型,然后输入优化代码:
(defun foo (x)
(check-type x fixnum)
(locally
(declare (fixnum x)
(optimize (safety 0)))
x))
LOCALLY
用于局部声明。
既然你在问:
Is it possible to use (declare (type ...))
declarations in functions but also perform type-checking on the function arguments, in order to produce faster but still safe code?
在我看来,您遗漏了关于 Common Lisp 类型系统的重要一点,即该语言不是(也不可能)是静态类型的。让我们澄清一下这方面。
编程语言大致可分为三大类:
- 静态类型检查:每个表达式或语句都是
在编译时检查类型正确性,以便类型错误
程序开发时可以检测到,代码比较多
高效,因为必须在 运行 时间完成类型检查;
- 使用动态类型检查:每个操作都在 运行 次检查
为了类型正确性,所以在 运行 时间不会发生类型错误;
- 没有类型检查:类型错误可能发生在 运行 时间,因此
程序可以因错误或有未定义的行为而停止。
已编辑
相对于之前的分类,Common Lisp 规范将决定他们是否要遵循第二种或第三种方法的重担留给了实现!不仅如此,而且通过 optimize
声明,规范允许实现在同一程序中自由地动态更改它。
因此,大多数实现在初始优化和安全级别上都采用第二种方法,并增加了以下两种可能性:
可以请求编译器在编译某些代码时省略 运行 时间类型检查,通常是出于效率原因,因此,在该特定代码中一段代码,并且取决于优化和安全设置,语言可以表现得像第三类语言:这可以通过类型声明的提示来支持,例如变量 (declare (type fixnum x))
和值 (the fixnum (f x))
;
可以在 运行-time 到 check-type
期间在代码中插入显式类型检查测试 ,因此,检查值类型的最终差异将导致“可纠正错误”。
此外,请注意,不同的编译器在编译时检查类型时可能表现不同,但它们永远不会达到具有静态类型检查的语言的编译器级别,因为 Common Lisp是一种高度动态的语言。例如考虑这个简单的案例:
(defun plus1(x) (1+ x))
(defun read-and-apply-plus1()
(plus1 (read)))
其中无法对调用 (plus1 (read))
进行静态类型检查,因为 (read)
的类型在编译时未知。
是否可以在函数中使用 (declare (type ...))
声明,同时对函数参数执行类型检查,以便生成更快但仍然安全的代码?
例如,
(defun add (x y)
(declare (type fixnum x y))
(the fixnum x y))
当被称为 (add 1 "a")
时会导致未定义的行为,所以我最好将其修改为
(defun add (x y)
(declare (type fixnum x y))
(check-type x fixnum)
(check-type y fixnum)
(the fixnum x y))
但我担心编译器被允许假定 check-type
总是通过并因此省略检查。
所以我的问题是,上面的例子是否如我预期的那样错误,其次,是否有任何常用的惯用语*用于通过优化代码实现类型安全?
*) 我可以想象,例如,使用优化的 lambda 并在进行类型检查后调用它,但我想知道这是否是最优雅的方式。
您始终可以先检查类型,然后输入优化代码:
(defun foo (x)
(check-type x fixnum)
(locally
(declare (fixnum x)
(optimize (safety 0)))
x))
LOCALLY
用于局部声明。
既然你在问:
Is it possible to use
(declare (type ...))
declarations in functions but also perform type-checking on the function arguments, in order to produce faster but still safe code?
在我看来,您遗漏了关于 Common Lisp 类型系统的重要一点,即该语言不是(也不可能)是静态类型的。让我们澄清一下这方面。
编程语言大致可分为三大类:
- 静态类型检查:每个表达式或语句都是 在编译时检查类型正确性,以便类型错误 程序开发时可以检测到,代码比较多 高效,因为必须在 运行 时间完成类型检查;
- 使用动态类型检查:每个操作都在 运行 次检查 为了类型正确性,所以在 运行 时间不会发生类型错误;
- 没有类型检查:类型错误可能发生在 运行 时间,因此 程序可以因错误或有未定义的行为而停止。
已编辑
相对于之前的分类,Common Lisp 规范将决定他们是否要遵循第二种或第三种方法的重担留给了实现!不仅如此,而且通过 optimize
声明,规范允许实现在同一程序中自由地动态更改它。
因此,大多数实现在初始优化和安全级别上都采用第二种方法,并增加了以下两种可能性:
可以请求编译器在编译某些代码时省略 运行 时间类型检查,通常是出于效率原因,因此,在该特定代码中一段代码,并且取决于优化和安全设置,语言可以表现得像第三类语言:这可以通过类型声明的提示来支持,例如变量
(declare (type fixnum x))
和值(the fixnum (f x))
;可以在 运行-time 到
check-type
期间在代码中插入显式类型检查测试 ,因此,检查值类型的最终差异将导致“可纠正错误”。
此外,请注意,不同的编译器在编译时检查类型时可能表现不同,但它们永远不会达到具有静态类型检查的语言的编译器级别,因为 Common Lisp是一种高度动态的语言。例如考虑这个简单的案例:
(defun plus1(x) (1+ x))
(defun read-and-apply-plus1()
(plus1 (read)))
其中无法对调用 (plus1 (read))
进行静态类型检查,因为 (read)
的类型在编译时未知。