【问题标题】:Get the type information in macros获取宏中的类型信息
【发布时间】:2015-06-08 09:39:43
【问题描述】:
(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?

【问题讨论】:

    标签: types macros common-lisp compile-time


    【解决方案1】:

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

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

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

    但是函数返回值的类型是什么?信息可能来自类型声明或类型推断。后者在 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 中(因为它们不在标准中)。

    通常在宏扩展时使用此类类型信息会打开一大堆蠕虫。对于基本用途,我建议考虑运行时类型并创建运行时类型分派的代码 - 除非您真的在编译时确实需要类型信息。

    【讨论】:

      【解决方案2】:

      除了Rainer's excellent answer,请注意Common Lisp 包括函数constantp,它采用形式和环境。这不会涵盖所有情况,因为有人可以使用变量而不是常量形式调用您的宏,但是如果您确实获得了常量形式,则可以在编译时检查其类型:

      (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
      

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

      或者我应该让我的宏扩展成为一个表单,它也调用 字体大小写?

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-09-03
        • 1970-01-01
        • 1970-01-01
        • 2015-08-15
        • 2015-06-28
        • 1970-01-01
        • 1970-01-01
        • 2018-07-18
        相关资源
        最近更新 更多