Macrocall.

;===============================================================
;
;
;         Generalized syntax for macro parameters.
;         ========================================
;
; Let us take a look on simple and typical macro call:


                 ((lambda-macro(X Y)
                      (println "X=" X "; Y=" Y))
                      
                      (+ 1 2)
                      (- (* 4 5)))


; This macro call should produce output
;
;          X=(+ 1 2); Y= (- (* 4 5))
;     
; I'll try to define version of lambda-macro that accepts parameters
; in more general form: for example, one might find compelling
; to define
;
;          ((lambda-macro L
;                         (println "L=" L))
;
;                         (+ 1 2)
;                         (- (* 4 5)))
;
; and output should be
;
;          L=((+ 1 2) (- (* 4 5)))
;
; or
;
;         ((lambda-macro (X (Y Z))
;                        (println "X=" X "; Y=" Y "; Z=" Z))
;
;                        (+ 1 2) (- (* 4 5)))
;
; And output is
;
;          X=(+ 1 2); Y=-; Z=(* 4 5)
;
; So, improvement is in the way values are assigned to the parameters
; of the macros X, Y, Z and L. Some kind of unification will be
; used.
;
;
;             Macrocall instead of lambda-macro.
;             ==================================   
;
; It would be complicated to insist on syntax above; I'd
; have to write my own interpreter if I want it understands new
; lambda-macro. Because of that, I'll use syntax inspired partly
; with common Lisp and partly with Pico Lisp. Instead of
;
;               ((lambda-macro(X Y)
;                    (println "X=" X "; Y=" Y))
;                    
;                    (+ 1 2)
;                    (- (* 4 5)))
;
; I'll use
;
;        (macrocall '((X Y)(println "X=" X "; Y=" Y))
;                   '((+ 1 2) (- (* 4 5))))
;
; The first list '((X Y)(println "X=" X "; Y=" Y)) can be
; understood as macro definition; also, I'll use
;
;        (macrocall '(L (println "L=" L))
;                   '((+ 1 2) (- (* 4 5))))
;           
; (macrocall '((X (Y Z)) (println "X=" X "; Y=" Y "; Z=" Z))
;            '((+ 1 2) (- (* 4 5))))
;
;
;                Implementation of macrocall
;                ===========================
;
; How can we do that? We know that let can be defined using
; lambda. On similar way, lambda can be defined using let.
; And on very similar way, lambda-macro can be defined using let.
;
; As our macrocall is like lambda-macro with different syntax,
; it should generate and then evaluate following expression:


              (let ((X '(+ 1 2))
                    (Y '(- (* 4 5))))
                   (println "X=" X "; Y=" Y))


; I had a luck. Newlisp has function "unify" that simplify my
; job here.
;
; For example,
;
;    (unify '(X Y) '((+ 1 2) (- (* 4 5))))
;
; results in
;
;    ((X (+ 1 2)) (Y (- * 4 5)))
;
; It is already similar to (let ((X '(+ 1 2))) ... )
;
; Implementation of macrocall function is routineous, so it doesn
; deserve comment or even thinking about.

(set 'quote-second (lambda(x)
                        (list (first x)
                              (apply quote (rest x)))))
                              
(set 'equivalent-let-block
     (lambda (definition arguments)
             (list 'let
                    (map quote-second
                         (unify (first definition)
                                        arguments))
                    (last definition))))

(set 'macrocall
     (lambda (definition arguments)
             (eval (equivalent-let-block definition arguments))))
             

; OK, now we can test macrocall to see if it works.


(macrocall '((X Y)(println "X=" X "; Y=" Y))
           '((+ 1 2) (- (* 4 5))))

(macrocall '(L (println "L=" L))
           '((+ 1 2) (- (* 4 5))))
                    
(macrocall '((X (Y Z)) (println "X=" X "; Y=" Y "; Z=" Z))
           '((+ 1 2) (- (* 4 5))))
                     
                     
; X=(+ 1 2); Y=(- (* 4 5))
; L=((+ 1 2) (- (* 4 5)))
; X=(+ 1 2); Y=-; Z=(* 4 5)
;
; It works
;
;                 From macros to functions
;                 ========================
;
; And, what about function calls? Functioncall as analogous to
; macrocall can be easily defined. But, I'll do something else -
; redefine macrocall so if I want some argument to be evalluated in
; caller environment, I'll denote respective parameter with X#
; instead of X, Y# instead Y etc.
;
;      (macrocall '((X# Y#)(println "X=" X "; Y=" Y))
;                 '((+ 1 2) (- (* 4 5))))
;
; Is it complicated? Not at all - I just need to adjust the function
; quote-second so equivalent-let-block produces

(let ((X (+ 1 2))
      (Y (- (* 4 5))))
     (println "X=" X "; Y=" Y))

; I.e. without quotes that prevetn evaluation of (+ 1 2)

(set 'quote-second (lambda(x)
                       (if (ends-with (string (first x)) "#")
                            (list (sym (chop (string (first x))))
                                  (last x))
                            (list (first x)
                                  (apply quote (rest x))))))
                              
(macrocall '((X# Y#)(println "X=" X "; Y=" Y))
           '((+ 1 2) (- (* 4 5))))
           
; X=3; Y=-20    - It works
;
; I should add that it is not complicated to write macros that
; behave like functions in standard Newlisp. Just parameters
; have to be evaluated somewhere in the body, so macrocall still
; has some syntactical advantage.
;
;
;              Between macros and functions.
;              =============================
;
; Also, it is possible to produce "crossbreeds", macros/fuctions
; that force evaluating some - but not all - arguments of the macro
; calls before assigning it to the parameter.


(macrocall '((X (Y Z#)) (println "X=" X "; Y=" Y "; Z=" Z))
           '((+ 1 2) (- (* 4 5))))

; X=(+ 1 2); Y=-; Z=20   - It works

;                      Conclusion
;                      ==========
;
;
; Macrocall is developed with so little substantial code that
; its advantages are surprising.
;
; (1) no need for special syntax for macro-definitions. If  
;     "macrocall" is defined, one does not need to write lambda
;     or lambda-macro ever again. Unfortunately, he has to write
;     "macrocall" all the time. It can be shortened on one
;     character - but for complete elimination, one should
;     make the change in the interpreter.
;
; (2) arguments are assigned to parameters on general and
;     natural, unification inspired way.
;
; (3) functions and "crossbreeds" between functions and macros
;     can be defined as well using "macrocall." X#, Y# etc syntax
;     for function-like parameters is somehow arbitrary. I can
;     think about better syntax, but I decided to leave that for
;     further articles.





No comments:

Post a Comment