获取宏中的类型信息

Get the type information in macros

(defmacro test (&key list &environment env)
  (typecase (get-type list env)
    (list `(do-something (list ,@list)))
    (integer `(do-something (list ,list ,list ,list)))))

(test :list '(1 2 3)) ; => (do-something (list 1 2 3))
(test :list (* 1 2)) ; => (do-something (list (* 1 2) (* 1 2) (* 1 2)))

是否可以在宏展开时获取 list 的类型? (我叫什么get-type

对求值不起作用,因为此时变量可能不存在,可能会改变一些其他值,不应该改变。

或者我应该让我的宏扩展成为一种形式,它也称为 typecase?

大多数 Lisp 方言都可以看作是动态类型的。 Common Lisp 也不例外。添加了类型和类型声明以允许在运行时处理类型:用于运行时类型检查和运行时类型分派。它还允许 Common Lisp 实现在编译时使用类型信息来生成更快的代码。使用类型信息进行编译时类型检查或在宏中的用户级别不是目标。尽管至少在早期 CMUCL(以及后来的 SBCL 和 Scieneer CL)使用类型声明和类型推断来进行一定数量的编译时类型检查。大多数其他实现不进行编译时类型检查,而少数实现类型推断。

请注意,您可以在运行时添加或重新定义类型。类型层次结构也可以在运行时更改(例如 class 层次结构)。

如果你有一个实际的对象,可以确定 Lisp 系统在编译时认为它是什么类型。

但是函数 return 值的类型是什么?该信息可能来自类型声明或类型推断。后者在 Common Lisp 中不可移植 - 可能有特定于实现的方法从类型推断中获取信息或使用库中的类型推断器。

关于函数类型声明的信息,一些 Common Lisp 实现提供了一个函数 FUNCTION-INFORMATION(最初为 ANSI CL 提出,但没有进入实际标准)。

在 LispWorks 6.1 中:

CL-USER 42 > (compile (defun foo () (the fixnum 3)))
FOO
NIL
NIL

CL-USER 43 > (declaim (ftype (function () fixnum) foo))
T

CL-USER 44 > (function-information 'foo)
:FUNCTION
NIL
((FTYPE FUNCTION NIL FIXNUM))

另见 VARIABLE-INFORMATIONFUNCTION-INFORMATIONVARIABLE-INFORMATION 也记录在 CLtL2 中,但不是 Common Lisp Hyperspec(因为它们不在标准中)。

通常在宏扩展时使用此类信息进行操作会带来一大堆麻烦事。对于基本使用,我建议从运行时类型的角度考虑并创建运行时类型调度的代码——除非你真的需要在编译时类型信息。

除了 , do be aware that Common Lisp includes the function constantp 外,还需要一种形式和环境。这不会涵盖所有情况,因为有人可以使用变量而不是常量形式调用您的宏,但是如果您 do 获得常量形式,您可以检查其类型编译时间:

(defmacro foo (arg &environment environment)
  (if (constantp arg environment)
      (typecase arg
        (string 'string-expansion)
        (vector 'vector-expansion)
        (number 'number-expansion)
        (t 'constant-but-other-type-expansion))
      'nonconstant-expansion))

CL-USER> (macroexpand '(foo "hello"))
STRING-EXPANSION
T
CL-USER> (macroexpand '(foo #(1 2 3)))
VECTOR-EXPANSION
T
CL-USER> (macroexpand '(foo 42))
NUMBER-EXPANSION
T
CL-USER> (macroexpand '(foo 'something-else))
CONSTANT-BUT-OTHER-TYPE-EXPANSION
T
CL-USER> (macroexpand '(foo bar))
NONCONSTANT-EXPANSION
T

现在,这可能看起来有点迂回,但它在某些地方可能很有用。

Or should I let my macro-expansion be a form, that also calls typecase?

如果您查看序列操作函数的开源实现,您可能会发现一些非常 有趣的代码示例。许多 Common Lisp 函数(例如,reduce)接受序列,这些序列可以是列表或向量。不幸的是,code 在这两种类型上有效地实现 reduce 是不同的。例如,看看 SBCL's sequence functions。您将开始了解他们如何处理此问题。您可能还想看看编译器宏。