Conversions to Functions and Macros.

;===============================================================
; CONVERSION OF FUNCTIONS TO MACROS AND VICE VERSA
;
;
; If you think on the way I do, then you sometimes have the function,
; and you want macro - to avoid using of one extra quote in the
; function call. Or, you have a macro, and you want function to pass
; it value stored in some variable.

; In this post, I'll define two cute functions for exactly that purpose;
;
; macro-from-function
; function-from-macro

; Lets start with one meaningless function and macro for illustration
; of the idea.

(set 'set-first-to-0-function
      (lambda()
         (set-nth ((first (args)) 0) 0)))

(set 'set-last-to-0-macro
     (lambda-macro()
         (set-nth ((first (args)) -1) 0)))
         
; The definitions are nearly equal, the difference is
; in the way we call these two:

(println (set-first-to-0-function '(1 2 3 4 5))) ; (0 2 3 4 5)
(println (set-last-to-0-macro (1 2 3 4 5)))   ; (1 2 3 4 0)

;---------------------------------------------------------------
; The similarity of these two definitions is striking, but it
; is not completely trivial to convert one to another - because
; lambda and lambda-macro definitions are not simply lists with
; the first elements lambda or lambda-macro. They are more something
; like special kinds of lists, as it can be seen from following
; expressions:

(println (first (lambda(x)(print x)))) ;(x)
(println (rest (lambda(x)(print x)))) ;((print x))

; Cons can surprise you:

(println (cons (lambda-macro) (lambda()(print))))

(lambda (lambda-macro ) () (print))

; But append behaves nice, so, it is maybe the most natural
; way to exchange lambda and lambda-macro:

(println (append (lambda-macro) (lambda(x)(print x))))

; result: (lambda-macro(x)(print x))

(set 'function-from-macro (lambda()
                             (append '(lambda) ; quote can be omitted
                                      (first (args)))))
(set 'macro-from-function (lambda()
                             (append '(lambda-macro)
                                      (first (args)))))
                                         
;---------------------------------------------------------------
; Does it work?

(set 'set-first-to-0-macro
     (macro-from-function set-first-to-0-function))
     
(set 'set-last-to-0-function
     (function-from-macro set-last-to-0-macro))

(println (set-first-to-0-macro (1 2 3 4 5))) ; (0 2 3 4 5)
(println (set-last-to-0-function '(1 2 3 4 5)))   ; (1 2 3 4 0)

;===============================================================
; CONVERSION OF BUILT-INS to LAMBDA AND LAMBDA-MACRO EXPRESSIONS.

; In Newlisp, there is no clear distinction between built in
; functions and built in macros. The program cannot test whether
; built in is function or macro, and in the reference manual,
; all of them are called functions. But some of the built ins
; really behave as functions, while some behave more like
; macros. For example, "reverse" is function. You have to call it
; with argument that evaluate to list, not list itself.

(println (reverse '(1 2 3)))

; Unlike "reverse", "for" behaves like macro:

(for (i 7 9) (println i))

; obviously, the expressions (i 7 9) and (println i) are not evaluated
; prior to the call of the "for."
;
; So, one might want to use macro version of reverse, something like
;
;                 (reverse-macro (1 2 3))
;
; or functional version of for:
;
;                 (for-function '(i 7 9) '(println i))
;
; Unfortunately, our function-from-macro and macro-from-function
; do not work for "reverse" and "for." They need lambda and
; lambda-macro expressions.

; We'll solve this problem by writing two functions that transform
; built in functions in lambda expressions, and built in macros in
; lambda-macro expressions. After that, function-from-macro and
; macro-from-function can be applied.

; How can we define the lambda and lambda-macro expressions
; doing exactly the same thing as reverse and for?

; These are some possibilities:

(lambda()
   (apply 'reverse $args))
   
(lambda-macro()
   (eval (cons 'for $args)))
           
; Let's test it:

(println "======================================================")
(println ((lambda()
           (apply 'reverse $args))
           '(1 2 3)))

; and

((lambda-macro()
      (eval (cons 'for $args)))
 (i 7 9)
 (println i))
 
; RESULT:
; (3 2 1)
; 7
; 8
; 9

; It works.

; It is obviously the trick; but it serves its purpose.
; Now, we can automatize such transformation of built-ins into
; equivalent lambda and lambda-macro expressions:

(println "======================================================")

(set 'lambda-form
     (lambda(built-in-name)
       (expand '(lambda()(apply 'built-in-name $args))
               'built-in-name)))
               
(set 'lambda-macro-form
     (lambda(built-in-name)
        (expand '(lambda-macro()(eval (cons 'built-in-name $args)))
                'built-in-name)))

; Does it work?

(set 'reverse-macro (macro-from-function (lambda-form 'reverse)))
(set 'for-function (function-from-macro (lambda-macro-form 'for)))

(println (reverse-macro (1 2 3)))
(for-function '(i 1 100) '(print i))

; It does.

(exit)

No comments:

Post a Comment