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.

JustAFellowCoder
  • 300
  • 2
  • 11
  • 1
    What is `someExpression` that "_does not seem to work?_" [MCVE]. 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. – ad absurdum Apr 19 '21 at 05:12
  • @adabsurdum I have edited the question to clarify what I mean. – JustAFellowCoder Apr 19 '21 at 07:44
  • You need to explain why you'd want to use a macro for this: what code transformation are you trying to achieve? –  Apr 19 '21 at 08:58
  • 1
    `(print 1)` is a valid Lisp form. `((print 1))`, `(((print 1)))`, ... `(((((print 1)))))` are not valid Lisp forms. – Rainer Joswig Apr 19 '21 at 11:16
  • @tfb I want to use a macro because I want to use a macro. I am not sure what you are asking as the question does not need that to be answered, especially if it is hypothetical. What I am trying to achieve as I have explicitly said is, in this case to simply print 1 as output. In general, execute what is sent as the parameter of foo, as I have also stated. – JustAFellowCoder Apr 19 '21 at 14:43
  • @RainerJoswig I am getting a (print 1) is not a valid function name when doing eval. – JustAFellowCoder Apr 19 '21 at 14:44
  • @JustAFellowCoder: that's what I' saying. You are not evaluating `(print 1)`. You are evaluating something else. Take a look at my prior comment for a hint. – Rainer Joswig Apr 19 '21 at 14:46
  • @RainerJoswig So I am evaluating ((print 1)) instead of (print 1). How do I form (print 1) from ((print 1))? – JustAFellowCoder Apr 19 '21 at 14:47
  • Best by not creating `((print))` in the first place. Then you don't need to remove anything. – Rainer Joswig Apr 19 '21 at 14:49
  • @RainerJoswig I am not saying what is best. What is best would be using a function instead of a macro most likely. But in this specific case, I want to use a macro for this and be given something like ((print 1)). – JustAFellowCoder Apr 19 '21 at 14:50
  • 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? – Rainer Joswig Apr 19 '21 at 14:51
  • You need to learn what macros are useful for, which is not this. And also why not to use `eval`. But `(defmacro serves-no-possible-useful-purpose (form) form)`: the code transformation you want is the identity transformation. –  Apr 19 '21 at 14:52
  • @RainerJoswig where? Changing `(eval (,someExpression)) to (eval ,someExpression) still gives a not a valid function error. – JustAFellowCoder Apr 19 '21 at 14:54
  • works for me actually – Rainer Joswig Apr 19 '21 at 14:55
  • 1
    `(defmacro foo (someExpression) `(eval ,someExpression))` -> `FOO`. `(foo (print 1))`-> `1 1` – Rainer Joswig Apr 19 '21 at 14:56
  • 1
    Then next you can try to see the difference of: `(defmacro foo (someExpression) `(eval ',someExpression))` – Rainer Joswig Apr 19 '21 at 14:58

1 Answers1

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.

  • 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. – JustAFellowCoder Apr 19 '21 at 20:21