0

So using common lisp, I want to be able to do something of the sorts of:

(defmacro foo (count &rest someExpression)
    `(do
        ((,count 0 (+ ,count 1)))
        ((= ,count 5) T)
        `(eval ,someExpression)
    )
)
(foo (print 1) temp)

With the result of it printing 1 5 times. I do not want to simply call (print 1) directly, but by passing the expression through a macro parameter and calling it via the macro. In other words, the macro foo should handle any expression(s) as input and run it. This case does not seem to work.

Edited to clarify an explicit script and intended function.

16
  • 1
    What is someExpression that "does not seem to work?" minimal reproducible example. Why is someExpression wrapped in parentheses when the macro is expanded? Consider someExpression as (+ 1 2 3): (eval ((+ 1 2 3))) is probably not the desired expansion. Commented Apr 19, 2021 at 5:12
  • 1
    (print 1) is a valid Lisp form. ((print 1)), (((print 1))), ... (((((print 1))))) are not valid Lisp forms. Commented Apr 19, 2021 at 11:16
  • 1
    You are not given ((print 1)). You are given (print 1). Your macro adds the extra parentheses. It puts the form into a list. Why? Commented Apr 19, 2021 at 14:51
  • 1
    (defmacro foo (someExpression) (eval ,someExpression))` -> FOO. (foo (print 1))-> 1 1 Commented Apr 19, 2021 at 14:56
  • 1
    Then next you can try to see the difference of: (defmacro foo (someExpression) (eval ',someExpression))` Commented Apr 19, 2021 at 14:58

1 Answer 1

2

Starting with your recent version, which is at least a reasonable candidate for a macro unlike the older one:

(defmacro foo (someExpression count-var)
  `(do ((,count-var 0 (+ ,count 1)))
       ((= ,count-var 5) T)
     `(eval (,someExpression))))

Well what is the expansion of (foo (print 1) c)?

(foo (print 1) x)
 -> (do ((x 0 (+ x 1))) ((= x 5) t)
      `(eval (,someexpression)))

Well, that's a disaster: what is that nested backquote doing? Let's just remove it:

(defmacro foo (someExpression count-var)
  `(do ((,count-var 0 (+ ,count 1)))
       ((= ,count-var 5) T)
     (eval (,someExpression))))
(foo (print 1) x)
 -> (do ((x 0 (+ x 1))) ((= x 5) t)
      (eval ((print 1))))

That's less disastrous, but the eval form is entirely bogus. We can make that 'work' by changing it to be at least syntactically legal:

(defmacro foo (someExpression count)
  `(do ((,count 0 (+ ,count 1)))
       ((= ,count 5) T)
     (eval ,someExpression)))

And now

(foo (print 1) x)
 -> (do ((x 0 (+ x 1))) ((= x 5) t)
      (eval (print 1)))

And this will 'work' but it will work purely by coincidence: because (print 1) returns 1 and the value of 1 is 1.

(foo (print 'foo) x)
  -> (do ((x 0 (+ x 1))) ((= x 5) t)
       (eval (print 'foo)))

and that's a run-time error.

But ... why are you using eval? eval is a terrible, terrible solution to almost any problem you can think of, unless the solution to the problem is called 'code injection attack', and in this case it's not just terrible: it's wrong. So we just remove it.

(defmacro foo (someExpression count)
  `(do ((,count 0 (+ ,count 1)))
       ((= ,count 5) T)
     ,someExpression))

And now

(foo (print 'foo) x)
 -> (do ((x 0 (+ x 1))) ((= x 5) t)
      (print 'foo))

Which looks like the code transformation we want. So, finally:

> (foo (print 'foo) x)

foo 
foo 
foo 
foo 
foo 
t

Which is, finally, fine. And this works:

> (foo (print x) x)

0 
1 
2 
3 
4 
t

As with yet another edit to the question it probably is more useful to put the variable name first and allow a bunch of expressions:

(defmacro foo (count-var &body forms)
  `(do ((,count-var 0 (+ ,count-var 1)))
       ((= ,count-var 5))
     ,@forms))

This will now allow multiple expressions in the body. And we could go further: we could allow it to specify the number of iterations and the return value`:

(defmacro foo ((count-var &optional (count 1) (value 'nil)) &body forms)
  `(do ((,count-var 0 (1+ ,count-var)))
       ((= ,count-var ,count) ,value)
     ,@forms))

And now

> (foo (x 2)
    (print x)
    (print (* x 2)))

0 
0 
1 
2
nil

Well, the name of this macro is dotimes of course.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you. This clarified everything in perfect detail. As it was, the parentheses were breaking it as well as missing ,@ in the case of &rest and the eval function was useless.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.