Get the type information in macros

2019-07-19 07:27发布

(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)))

Is it possible to get the type of list at macroexpansion-time? (what I called get-type)

It won't work with evaluation, because the variables may not be existent at this time, and maybe some other values will be changed, that should not be changed.

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

2条回答
仙女界的扛把子
2楼-- · 2019-07-19 07:42

In addition to Rainer's excellent answer, do be aware that Common Lisp includes the function constantp which takes a form and an environment. This wont' cover every case, since someone could call your macro with a variable rather than a constant form, but in the case that you do get a constant form, you can check its type at compile time:

(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

Now, this may seem a bit roundabout, but it can be useful in some places.

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

You may find some very interesting code examples if you look in open source implementations of sequence manipulation functions. Many Common Lisp functions (e.g., reduce) take sequences, and these can be either lists or vectors. Unfortunately, the code to implement reduce efficiently on these two types is different. E.g., have a look at SBCL's sequence functions. You'll start to get a feel for how they handle this issue. You may also want to have a look at compiler macros.

查看更多
甜甜的少女心
3楼-- · 2019-07-19 07:53

Most Lisp dialects can be seen as dynamically typed. Common Lisp is no exception. Types and type declarations have been added to allow to deal with types at runtime: for runtime type checks and runtime type dispatch. It also allows Common Lisp implementations to use type information at compile time to generate faster code. Using type information for compile-time type checking or at the user level in macros was not a goal. Though early on at least CMUCL (and later SBCL and Scieneer CL) used type declarations and type inference for some amount of compile-time type checking. Most other implementations don't do compile-time type checking, while a few do type inference.

Note that you can add or redefine types at runtime. Also type hierarchies can change at runtime (class hierarchies for example).

If you have an actual object, one could determine the type the Lisp system thinks it is at compile time.

But what is the type of a function return value? The information could come from a type declaration or from type inference. The latter is not portable in Common Lisp - there might be implementation specific ways to get information from type inference or to use a type inferencer from a library.

For information about type declarations of functions some implementations of Common Lisp provide a function FUNCTION-INFORMATION (originally proposed for ANSI CL, but didn't make it into the actual standard).

Here in 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))

See also VARIABLE-INFORMATION. FUNCTION-INFORMATION and VARIABLE-INFORMATION are also documented in CLtL2, but not the Common Lisp Hyperspec (since they are not in the standard).

Generally operating with such type information at macro expansion time opens up a whole can of worms. For basic use I would recommend to think in terms of runtime types and create code which runtime type dispatching - unless you really really need type information at compile-time.

查看更多
登录 后发表回答