(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?
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:
Now, this may seem a bit roundabout, but it can be useful in some places.
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.
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:
See also
VARIABLE-INFORMATION
.FUNCTION-INFORMATION
andVARIABLE-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.