;===============================================================
;
;
; 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.
Macrocall.
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment