schemelisprackets-expression

How to convert a list to code/lambda in scheme?


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

  • 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.

    (define expr '(+ (* p p) (* x x)))
    
    (eval `(define (fun x p) ,expr))
    

    If you leave away the (eval ...) layer, you see:

    `(define (fun x p) ,expr)
    ;; '(define (fun x p) (+ (* p p) (* x x)))
    

    The code, which is actually evaluated by the eval.

    Since this eval takes place in the global environment level, no side effects are to be feared.

    Below are my more complicated solutions with a define-expr macro. 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:

    (define expr '(+ (* p p) (* x x)))
    
    (define-syntax-rule (define-expr args expr)
      (define args expr))
    
    (eval `(define-expr (fun x p) ,expr))
    

    This call executes in the background:

    (define (fun x p) (+ (* p p) (* x x)))
    

    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 fun function:

    (fun 3 4) ;; 25
    

    In common lisp

    Personally, I prefer common lisp's macro system. it is more direct.

    (defparameter *expr* '(+ (* p p) (* x x)))
    
    (defmacro defun-expr (fnname args expr)
      `(defun ,fnname ,args ,expr))
    
    (macroexpand-1 '(defun-expr fun (x p) *expr*)) ;; doesn't work
    ;; (DEFUN FUN (X P) *EXPR*) ;
    ;; T
    ;; you can see, that *EXPR* actually has to be evaluated once more
    
    (macroexpand-1 `(defun-expr fun (x p) ,*expr*)) ;; this however is correct
    ;; (DEFUN FUN (X P) (+ (* P P) (* X X)))
    
    ;; Thus when you call the macro, you have to execute it using eval:
    (eval `(defun-expr fun (x p) ,*expr*)) 
    ;; FUN ;; function is defined!
    
    (fun 3 4) ;; 25
    

    Since I am not that familiar with Racket's macro system, I did the macro construction in common lisp using the great macroexpand-1 which shows the code construct executed by the macro. And then transferred/guessed the corresponding define-syntax-rule in Racket.

    In Racket, macroexpand-1 is (syntax->datum (expand-once '<macrocall>)):

    (syntax->datum (expand-once `(define-expr (fun x p) ,expr)))
    ;; '(define (fun x p) (+ (* p p) (* x x)))