Let's say I have a list, for example: (define a '(+ (* p p) (* x x))).
How can I define a procedure with an expression given by a, for example here:
(define (H x p) (+ (* p p) (* x x)))) ?
I tried to do something like: (define (H x p) (eval a)) but it says that p and x are undefined. I guess, there's a simple workaround with apply or something similar, but I can't wrap my head around it.
I guess I could just modify p and x in my list according to the value passed to the procedure H and then evaluate the new list, but it's kind of ugly... Or maybe there's a pretty way to do this?
Solution in Racket
What you try to do is actually to inject the pre-defined function body list construct into the body of a function definition (macro) call.
If you leave away the
(eval ...)layer, you see:The code, which is actually evaluated by the
eval.Since this
evaltakes place in the global environment level, no side effects are to be feared.Below are my more complicated solutions with a
define-exprmacro. They explain, why it was so difficult to solve. After all that, I saw that one needs actually only an(eval `(define (<args>) ,expr)construct and actually no additional macro.Complicated Solution in Racket
You can do it in Racket, too:
This call executes in the background:
What you try to do is actually a dynamic macro. The problem with this is that you have to give the function body code in runtime. Somehow you need to evaluate the function body expression by one evaluation more than the rest of the macro. Thus, it is necessary to wrap the macro call by an
(eval `<macrocall ,component-to-be-evaluated-once-more>)I call this construct
eval-over-macro-call.After this you can call the so-defined
funfunction:In common lisp
Personally, I prefer common lisp's macro system. it is more direct.
Since I am not that familiar with Racket's macro system, I did the macro construction in common lisp using the great
macroexpand-1which shows the code construct executed by the macro. And then transferred/guessed the correspondingdefine-syntax-rulein Racket.In Racket,
macroexpand-1is(syntax->datum (expand-once '<macrocall>)):