Sorry for the clumsy title but i'm having a hard time describing what i'm looking for in a few words ...

I'm working on a Common Lisp DSL project and I'm wondering if the following is possible:

The DSL might have a couple of functions

(defun foo (&rest rest))

and

(defun bar (arg))

that'll be used in the following way:

(foo (bar 100) (bar 200) (bar 300) (bar 400) (bar 500)) etc.

Now, that's a lot of redundant typing, so I wonder if it's possible to create an expander macro that'll allow for

(foo (expand bar 100 200 300 400 500))

without changing foo itself ?

3

There are 3 answers

2
AudioBubble On BEST ANSWER

No, this can't be done without changing the signature of the functions you are defining (or possibly using some hairy thing which redefines macroexpansion): you have what I call a 'spread/nospread impedance mismatch' which can't be resolved with a standard macro in CL.

A 'nospread' function is a function which wraps all of its arguments into one formal. A 'spread' function has one formal per argument. I learned these terms when using InterLisp: they may predate that, but they seem to be mostly unused now. CL functions can be only partly-nospread ((foo bar &rest more)). Your foo is nospread.

A spread/nospread impedance mismatch is when you have a spread function but want a nospread one, or vice versa. It is almost always a sign of a design problem. The workaround for spread/nospread problems generally involves apply.

Your problem is that foo is a nospread function: it turns all its arguments into one list, but you want the macro to treat it as a spread function, handing it a single list of arguments.

In particular, in CL an expression like (x (y ...)) can never be turned into (x a1 a2 ...) for any y, function or macro (but see below), and this is what you need to happen.

In your case you want something like

(foo (expand bar a b ...)

To turn into

(foo (bar a) (bar b)

And that can't happen.

There have been Lisps which had things which were called 'splicing macros' in which a macro's expansion could be 'spliced' into a list, in the way that ,@ does for backquote. It may be that there are splicing macro packages for CL, and it may even be that they are portable: you can get a long way with *macroexpand-hook*. But standard CL macros can not do this.

2
wasserwerk On

There is no need for a macro:

(defun foo (&rest rest)
  (apply #'+ rest)) ; for example add all together 

(defun bar (arg)
  (1+ arg)) ; 1+ is only an example here

(apply #'foo (mapcar #'bar '(100 200 300 400 500)))
0
Dan Robertson On

It may be worthwhile to split your dsl into two parts, a simpler version you program against and a more user-friendly version. For example:

(in-package #:impl)
(defun foo (&rest args) ...)
(defun bar (arg) ...)

(in-package #:dsl)
(defun foo (&rest args) (apply #'impl:foo (append args)))
(defun bar (&rest args) (mapcar #'impl:bar args))
(foo (bar 100 200 300 400 500))