mystery quoting

Started by Fanda, November 27, 2006, 09:17:18 PM

Previous topic - Next topic

Fanda

I don't really understand, what's happening in this code and would be happy, if somebody could help me to get more insight:


(define-macro (qq)
  (println (args)))

(define-macro (q)
  (apply qq (args)))


Watch, how calling 'q' with a symbol or a quoted symbol adds another quote:
(q 3) => (3)
(q "abc") => ("abc")
(q x) => ('x)
(q 'x) => (''x)
(q ''x) => ('''x)


Why is that?



Thank you, Fanda

Fanda

#1
Hm...  It seems like 'apply' is doing it:
(apply qq '(a b c)) => ('a 'b 'c)
(apply qq '(1 2 3)) => (1 2 3)
(apply qq '("a" "b" "c")) => ("a" "b" "c")


Fanda



PS: I have never applied macro!

Fanda

#2
Try map:
> (map qq '(a b c))
('a)
('b)
('c)
(('a) ('b) ('c))


I believe that newLISP prepares the list - symbols inside it - by quoting them. It works for functions, but doesn't work for macros.



Fanda

nikus80

#3
I'm not very sure of what you're doing but macros arent supposed to be applied or mapped.

A macro doesn't evaluate his arguments, that means that, in newLISP, any call of a macro (mac a b c d) behaves like (mac 'a 'b 'c 'd) if mac were a normal function instead of a macro.

Lutz

#4
Macros can be mapped or applied, let me explain: Fanda's example boils down to this:


(define-macro (qq) (args))
> (qq a b c)
(a b c)
> (apply qq '(a b c))
('a 'b 'c)


Here the same definition, but as a normal function:


(define (qq) (args))
> (qq a b c)
(1 2 3)
> (apply qq '(a b c))
(a b c)


You see that the macro must behave like it does, or it would be a normal function evaluating it's arguments. The following comparison shows that macros can be applied and mapped and simulate the behavior of a normal function. First the behavior of the built-in +:



> (set 'a 1 'b 2 'c 3)

> (+ a b c)
6

> (apply + '(a b c))
value expected in function + : 'a

> (apply + '(1 2 3))
6

> (map + '(1 2 3) '(10 20 30))
(11 22 33)


Now the same as a macro:



(define-macro (my+) (apply + (map eval (args))))

> (my+ a b c)
6

> (apply my+ '(a b c))
value expected in function + : 'a

> (apply my+ '(1 2 3))
6

>  (map my+ '(1 2 3) '(10 20 30))
(11 22 33)


both produce the same results behaving the same way in map and apply situations.



Lutz

rickyboy

#5
By the way, I always wanted to get something clarified and so I might as well ask it now.  The manual says (under define-macro) that
QuoteMacros in newLISP are similar to define functions, but they do not evaluate their arguments. New functions can be created to behave like built-in functions that delay the evaluation of certain arguments. Since macros can access the arguments inside a parameter list, they can be used to create flow-control functions like those already built into newLISP.

Now, I've always taken this to mean that the only difference between lambda and lambda-macro is that the former takes on evaluated arguments and the latter takes on unevaluated arguments.  Is this right?



I was always under this impression (if correct or not) because nowhere in the newlisp runtime strategy could I see a separate macro expansion step (as in Common Lisp).  Everything seems to happen at run-time, i.e. dynamically.  The downside is that you can't pre-process your macro sexps, but the upside is that macros in newlisp are much easier to write, debug and understand than their Common Lisp counterparts.  I think the upside is much bigger than the downside, and a good design decision for newlisp.



Thanks for any clarification.
(λx. x x) (λx. x x)

nikus80

#6
I didn't mean you can't map or apply macro. What I meant to say is that if you are going to map 'n' apply your macro everywhere, it probably wants to be a function, not a macro.

if not, which would be a good example (I can't think of anything, but maybe I'm being too scheme/commonlispy



edit: BTW, when you're in the top level in common lisp, a macro also works dinamically. Macros work different in newLisp cause in common lisp you return a list of code which is then evaled in the place of the macro. In newlisp you don't need to do that because of dynamic scope, you don't return a list of code, you just execute it in place. Or am I wrong?

Lutz

#7
Yes Rickyboy, this is exactly how it is, evaluation of parameters is the only difference.



In Common LISP as a compiled language it makes sense to have expansion as part of the macro functionality, while in newLISP as a dynamic language you use extra functions for this, like 'expand' or 'letex' doing the work during runtime.



Often you can allleviate the runtime impact of expansion by using function currying or other function factory techniques. This way the expansion work is done only once when fabricating the function.



Having template expansion as a separate functionality means also that you can use it outside of macros, e.g. inside normal functions or even outside at the toplevel in a script file.



Lutz

rickyboy

#8
nikus80,



If my understanding of newlisp macros is correct (cf. my post above), they are not macros in the Common Lisp vein, just functions (i.e. lambdas) that take on unevaluated arguments.  Hence, the requirement for newlisp macros is not their potential "applicability", but rather if the programmer needs to delay evaluation of arguments during function application.



The advantage of having such "macros" is that they are as easy to reason with as functions, and as I said before, this is a big plus for application development.



Lutz will clarify this soon, no doubt.



All the best!  --Ricky
(λx. x x) (λx. x x)

rickyboy

#9
Ah!  Lutz came in with the clarification as I was (slowly) composing my last message.   Thanks once again, Lutz!  You are the faster gunslinger.



--Rickyboy
(λx. x x) (λx. x x)

Lutz

#10
Yes Rickyboy, exactly, see my previous post, we posted at the same moment (and again :)).



To Nikus80: dynamic scoping is not related to all this.



Lutz

nikus80

#11
No, what I meant to say is that newLisp-like macros are only useful with dynamic scope. They're useless with lexical scope since the code you receive often refers the current enviroment variables.

rickyboy

#12
I agree with Lutz -- I don't see what the usefulness of newlisp macros has to do with the scope (or extent) of variables.  The type of variable scope of a language such as newlisp has only to do with how the *free* variables are evaluated in an s-expression, not the *bound* variables.  The parameters of a function (or a newlisp macro) delineate the bound (i.e. not free) variables in the body of the lambda(-macro).  And it turns out that the variables bound by a lambda have the same treatment whether we have lexical or dynamic variable scope in our language.  It's the free variables we have to worry about -- they are treated differently if we have lexical versus dynamic scope.  Hence, any argument about how parameters, i.e. bound variables, are treated (as in the function-versus-macro issue we were talking about) has nothing to do with the variable scope's effect on the value of free variables.
(λx. x x) (λx. x x)

nikus80

#13
suppose newLisp had lexical scoping. Forget macros for a second.

imagine there is a function mydolist that receives a quoted code, and very much behaves like dolist


(set 'lst '(1 2 3))
(let (a 3)
    (mydolist '(e lst)
               '(+ a 3)))


when mydolist calls eval on '(+ a 3), what is the value of a? 3, you say. But only with dynamic scope. with lexical scope, if mydolist calls eval, it's evaled in the lexicals scope where mydolist was defined, which could be very different from the actual and where a could be undefined. So, with lexical scope, you have to receive a list and eval it in the current scope. Something like


(let (a 3)
   (eval (mydolist '(e lst) '(+ a 3))))

which, by the way, is exactly how common lisp macros work.

about appling macros, I thought about it and I could think of a place to use the technique. But it didn't worked.



(define-macro (my-or)

  (let (val (eval (args 0)))

    (if val val (apply my-or (rest (args))))))



this mimicks the recursive definition of (or) of scheme or common lisp.



This doesn't work because of the mystery quoting. If I call my-or with



(my-or nil (cons 2 3))

it returns the list (cons 2 3), not (2 3).



I think that, if applied. a macro should behave like a normal function. Usually, when you're appling a macro, you've already prevented it's argument from evaluation.



To my-or to work, you have to write it as a function, then make the macro a call to it.





(define (fun-my-or2 )

  (let (val (eval (args 0)))

   (if val

    val

    (apply fun-my-or2 (rest (args))))))



(define-macro (my-or2 )

  (apply fun-my-or2 (args)))



of course, i guess is no big deal. you could write a (define-recursive-macro) to do the hard work. But still, I think is not the desired behavior.

Lutz

#14
... or you could write your function like this:


(define-macro (my-or)
    (let (val (eval (args 0)))
       (if val val (apply my-or (rest (map eval (args)))))))

(my-or nil (cons 2 3)) => (2 3)


but the same logic than can be easier rewritten as a normal function, because all arguments are evaluated:


(define (my-or)
    (if (args 0) (args 0) (apply my-or (rest (args)))))

(my-or nil (cons 2 3)) => (2 3)


or even shorter


(define (my-or p)
    (if p p (apply my-or (args))))

(my-or nil (cons 2 3)) => (2 3)


Lutz



ps: note that none of the definitions (of you and me) handle the case where all arguments evaluate to nil, which could be handled like this:


(define (my-or p)
    (if p p (if (args) (apply my-or (args)) p)))