> Hi
>
> I have this macro:
>
> (defmacro format-time (one &optional two &key format-fn)
> (let ((one* (eval one))
> (two* (eval two)))
> (if two*
> `(,format-fn t "(~a:~a - ~a:~a)" ,@one* ,@two*)
> `(,format-fn t "~a:~a" ,@one*))))
You're trying to move a run-time problem to compile time. You have one
or two expressions that return a list of two items. But you would
like to pass these as two or four arguments, not one or two lists.
Those lists are not available at macro-expansion time, so this splicing
will do you no good, and the eval calls you have there are just trouble,
because they happen at macro-expansion time.
Firstly, the format language has gadgets for dealing with lists:
(format t "~gobbledygook" '(1 2) '(3 4))
Secondly, if you don't want to do that, you can solve this
with a simple function:
;; untested: exercise for reader
(defun format-time (one &optional two &key (format-fn #'format))
(if two
(funcall #'format-fn t "(~a:~a - ~a:a)"
(first one) (second one) (first two) (second two))
(funcall #'format-fn t "~a:~a" (first one) (second one))))
Functional abstraction is your first tool.
Based on this solution, you can develop a macro. But then you're
just doing a useless macro inlining of a function which just as well be be
achieved with (declaim (inline ....)).
(What is the reason for creating a macro? Do you need to control
the evaluation of forms, or interpret them forms other than
as Lisp expressions? Ah right, you want to specify the :function
argument as a simple symbol without quoting! That's why.)
So what might the macro solution look like?
Possibly along these lines:
;; untested: exercise for reader
(defmacro format-time (one &optional two &key (format-fn 'format))
(let ((one-sym (gensym))
(two-sym (gensym)))
(if two
`(let ((,one-sym ,one)
(,two-sym ,two))
(,format-fn t "(~a:~a - ~a:a)"
(first ,one-sym) (second ,one-sym)
(first ,tw-sym) (second ,two-sym))
`(let ((,one-sym ,one))
(,format-fn t "~a:~a" (first ,one-sym) (second ,one-sym)))))))
Note that we give the format-fn key argument a default value.
Most of the time you're going to use format, right? If the caller
forgets to specify the argument, you end up the expansion (nil ...)
which results in a mysterious error, like "no such function: nil".