Common lisp 用于参数检查和其他偏执狂的常见Lisp习语?

Common lisp 用于参数检查和其他偏执狂的常见Lisp习语?,common-lisp,design-by-contract,Common Lisp,Design By Contract,这个问题是关于编码约定、最佳实践和生产中的风格、任务关键型公共Lisp代码。我仔细阅读了Google的Common Lisp Style Guide(),但没有找到任何明确解决我的具体问题的方法,我通过示例和与C/C++、Java等的对比来表达。我还快速浏览了基于Github的常见Lisp代码,我没有看到很多参数检查和中间值检查,我在C/C++、Java、,等等 P>在我的商店里,我们习惯于检查论据和其他价值,并且在争论不符合合同的前提条件下采取早期退出方式。例如,考虑下面的(人为的,不完美的

这个问题是关于编码约定、最佳实践和生产中的风格、任务关键型公共Lisp代码。我仔细阅读了Google的Common Lisp Style Guide(),但没有找到任何明确解决我的具体问题的方法,我通过示例和与C/C++、Java等的对比来表达。我还快速浏览了基于Github的常见Lisp代码,我没有看到很多参数检查和中间值检查,我在C/C++、Java、,等等

<> P>在我的商店里,我们习惯于检查论据和其他价值,并且在争论不符合合同的前提条件下采取早期退出方式。例如,考虑下面的(人为的,不完美的,典型的,但请不要浪费时间批评,微观例子,这表明CL例子):

问题是Common Lisp中的实际生产代码是否会进行更多检查。例如,编写显式代码来检查s实际上是一个字符串并且实际上足够长,并且前两个字符实际上是“O!”,这可能是合理的

这段代码是否仅仅因为它是教学性的,就绕过了所有的偏执狂?任务关键型生产部署中的同一代码是否更有可能进行偏执检查(我对Github的CL代码进行的轻微调查表明“否”)?如果现实世界的CL代码不倾向于偏执狂,为什么不呢?角落案例或穷举测试的实践是否比表面上更普遍


简言之,我对风格上的差异感到困惑。现实世界中,任务关键型C代码往往是超级偏执狂。我在CL中看不到同样的情况。也许我没有看到正确的代码库?也许我没有读对书?这个问题的答案似乎不容易在谷歌上找到。

通用Lisp是一种专为开发大型复杂应用程序而设计的语言。在80年代被认为是大型应用。但它从生产系统中获得了一些处理错误的工具,甚至对编译时检查提供了一些支持。还有很多代码是为原型软件、研究系统和/或个人目的编写的。你不会发现质量总是很高的。还要记住,有时非常严格的检查可能会使代码过于严格(例如:许多HTTP客户机将发送不一致的请求,但事实就是这样,如果不丢失大量可能的用户,就无法轻松拒绝这些请求)

让我们来看一些例子,Common Lisp如何帮助您编写健壮的软件:

strong键入和运行时类型检查

我们期望一个普通的Lisp系统会对每个操作进行运行时检查。避免使用不需要的Lisp系统

如果您有一个数值函数:

(defun foo (n x)
  ....
    (bar ...))

(defun bar (a b)
  (+ a b))
如果
FOO
没有进行参数检查,我们希望
+
操作最终会检查参数。在运行时将出现错误,并运行错误处理程序,默认情况下,该处理程序将调用调试器

想想看:所有(大多数)操作都将在运行时被检查。所有对象都有一个基本类型标记(整数、字符串、数组、位向量、字符、流等),并且在运行时最终将检查该类型

但我们对Lisp运行时的期望更高:

  • 数组边界检查
  • 插槽类型检查
  • 发生错误时的堆一致性
  • 针对有害操作的各种检查,如重新定义标准函数、删除公共Lisp包、算术错误等
使用不进行运行时类型检查的Lisp系统是一个巨大的痛苦。现在,CommonLisp允许我们声明部分代码,而不是执行运行时检查。最佳策略:在不产生风险的情况下,找到最少量的代码(请参阅本地的

参数列表

CommonLisp允许在编译时进行一些参数列表检查。使用它

(defun foo (&key (n 1) (x 1.0))
  ...)
现在,一个典型的编译器将捕获一个调用,如
(foo:y2:x2.0)
,并出现一个错误:错误的关键字参数
:y

让编译器检查参数列表中的参数数量是否正确,以及使用的关键字参数是否正确

CLOS,通用Lisp对象系统

使用CLOS

(defmethod foo ((n integer) (x float)) ...)
如果您像上面那样定义一个方法,那么在运行时,方法体中的
n
将是一个整数,
x
将是一个浮点。如果您使用其他参数类型调用
FOO
,但没有应用任何方法,则会出现运行时错误

类似于示例插槽:您可以声明类型

(defclass bar ()
   ((x :type float)
    (n :type integer)))
使用一个通用的Lisp实现来实际检查这些声明或编写自己的检查

另外:不要基于列表创建原始数据结构。始终将它们打包到CLOS类和方法中。这样,您就可以获得适当数量的运行时检查和内省功能

在运行时检查类型

Common Lisp为运行时类型检查提供了宏:

CHECK-TYPE
宏允许奇特的类型检查,甚至修复错误

CL-USER 27 > (foo 2000 5)

Error: The value 5 of X is not of type FLOAT.
  1 (continue) Supply a new value of X.
  2 (abort) Return to level 0.
  3 Return to top loop level 0.

Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.

CL-USER 28 : 1 > :c 1

Enter a form to be evaluated: 5.0
请注意,您可以使用
DEFTYPE
和任意类型谓词定义自己的类型

使用发出错误信号的Lisp结构

例如,
ecase
案例

CL-USER 37 > (let ((code 10))
               (ecase code
                 (1 'fine)))

Error: 10 fell through ECASE expression.
Wanted one of (1).
ecase
在没有匹配的子句时自动发出错误信号

宏允许我们检查任意断言。

Common Lisp提供了一个内置宏

同样,可以进行一定量的运行时修复:

CL-USER 33 > (foo 2001 5.0)

Error: The assertion (AND (INTEGERP N) (EVENP N)) failed.
  1 (continue) Retry assertion with new value for N.
  2 (abort) Return to level 0.
  3 Return to top loop level 0.

Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.

CL-USER 34 : 1 > :c 1
Enter a form to be evaluated:
2000
98.38699
现在我们可以编写一个额外的方法来检查
n
是否大于
x

(defmethod setup-bar :before ((b bar) (n1 integer) (x1 float))
   (assert (> n x) (n x)))
:before方法将始终在主方法之前运行

将合同设计系统添加到CLOS中

这里有图书馆。这是一个exa
(check-type a1 (array * (3 3)))
CL-USER 37 > (let ((code 10))
               (ecase code
                 (1 'fine)))

Error: 10 fell through ECASE expression.
Wanted one of (1).
(defun foo (n x)
  (assert (and (integerp n) (evenp n)) (n))
  (assert (floatp x) (x))
  (* (isqrt n) (sqrt x)))
CL-USER 33 > (foo 2001 5.0)

Error: The assertion (AND (INTEGERP N) (EVENP N)) failed.
  1 (continue) Retry assertion with new value for N.
  2 (abort) Return to level 0.
  3 Return to top loop level 0.

Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.

CL-USER 34 : 1 > :c 1
Enter a form to be evaluated:
2000
98.38699
(defclass bar ()
   ((n :type integer)
    (x :type float))) 

(defmethod setup-bar ((b bar) (n1 integer) (x1 float))
   (with-slots (n x) b
      (setf n n1 x x1))
   b))
(defmethod setup-bar :before ((b bar) (n1 integer) (x1 float))
   (assert (> n x) (n x)))
(define-condition mailer-incomplete-delivery-error
          (mailer-error)
  ((recipient-and-status-list :initarg :recipient-and-status-list
                  :reader mailer-error-recipient-and-status-list)))