Macros and deficiencies from CL

Started by eliben, April 19, 2006, 02:02:27 PM

Previous topic - Next topic

eliben

#15
Quote from: "Lutz"Yes, here is another similar 'lazy' evaluating version (also written as a macro):



(define-macro (my-and)
    (catch (dolist (i (args))  
        (if (not (eval i)) (throw nil) true))))


now only tested elements up to the thrown are evaluated



Lutz


Is there no way to achieve this without catch / throw ?

rickyboy

#16
QuoteIs there no way to achieve this without catch / throw ?

Yes, there is a way: with recursion, my good man, with recursion.  :-)
(λx. x x) (λx. x x)

eliben

#17
Quote from: "rickyboy"
QuoteIs there no way to achieve this without catch / throw ?

Yes, there is a way: with recursion, my good man, with recursion.  :-)


So, the common idiom in newlisp for returning (breaking) in the middle of the loop is by using the exception ?

rickyboy

#18
Quote from: "eliben"So, the common idiom in newlisp for returning (breaking) in the middle of the loop is by using the exception ?


I like to think of 'catch/throw' as non-local exiting (i.e. I treat it in this more generic sense as a programming construct).  Non-local exits can be used (are most often used?) for exception handing.  However, non-local exits which are non-exceptional, if you will, are OK too.  Don't let the 'throw' keyword *throw* you.  (* rimshot *)  :-)



Lutz, any thoughts?
(λx. x x) (λx. x x)

eliben

#19
Quote from: "rickyboy"
Quote from: "eliben"So, the common idiom in newlisp for returning (breaking) in the middle of the loop is by using the exception ?


I like to think of 'catch/throw' as non-local exiting (i.e. I treat it in this more generic sense as a programming construct).  Non-local exits can be used (are most often used?) for exception handing.  However, non-local exits which are non-exceptional, if you will, are OK too.  Don't let the 'throw' keyword *throw* you.  (* rimshot *)  :-)




It just feels a little clunky to me that this 'catch' should wrap a loop to enable us to exit from it. Perhaps 'dolist' can be expanded by means of a macro to enable a more structured way to exit from a loop.

rickyboy

#20
Quote from: "eliben"It just feels a little clunky to me that this 'catch' should wrap a loop to enable us to exit from it. Perhaps 'dolist' can be expanded by means of a macro to enable a more structured way to exit from a loop.


I know.  I felt the same way when I found out that 'return' in CL works by the fact that all functions are wrapped with a 'block' tagged with the same name as the function.
(λx. x x) (λx. x x)

eliben

#21
Quote from: "rickyboy"
Quote from: "eliben"It just feels a little clunky to me that this 'catch' should wrap a loop to enable us to exit from it. Perhaps 'dolist' can be expanded by means of a macro to enable a more structured way to exit from a loop.


I know.  I felt the same way when I found out that 'return' in CL works by the fact that all functions are wrapped with a 'block' tagged with the same name as the function.


I guess this can't be too difficult to implement using macros in newlisp ?



Care to give it a shot  ?

rickyboy

#22
Quote from: "eliben"I guess this can't be too difficult to implement using macros in newlisp ?



Care to give it a shot  ?


Do you mean implementing an "expanded 'dolist'"?  Sure, I could create a 'dolist*' macro that you can '(return)' from, but am I allowed to implement it with 'catch/throw'?  That is, do you object to any use of 'catch/throw' for "non-exceptions" or is it OK if its use is under-the-hood and away from sight?  ;-)
(λx. x x) (λx. x x)

Lutz

#23
here is a solution:



(define-macro (dolist-while)
    (letn ( p (args)
            var (p 0 0)
            lst (p 0 1)
            cnd (p 0 2)
            body (p -1)
            res nil)

    (eval (expand
            '(catch (dolist (var lst)
                           (if (set 'res cnd) body (throw res) )))
            'var 'lst 'cnd 'body))
))

; try it

> (dolist-while (i (sequence 1 100) (< i 10)) (println i))
1
2
3
4
5
6
7
8
9
nil
>


It works, but unless I am doing programming language lab work, I would not do it, and just use 'catch/throw' explicitely.



This is different in a compiled language where you can do expansion before complilation. Of course you could run a pre-processsor in newLISP doing all the expansion first and then run the shorter, faster program.



And there are also the different forms of: while, until, do-while, do-until in newLISP for a more structured approach of interrupting loops.



Lutz



ps: note that in newLISP you can omit the () around the initializers in the 'letn' (but you don't need to)

Dmi

#24
Quote from: "eliben"But as I understand from you, such optimization is impossible in newlisp because macros are just like functions. I understand, and it's not a big deal really. However, wouldn't it be quite simple to implement, in your opinion ? A single "expansion" pass of the interpreter on the code, detecting and expanding all the instances of macros, can make this optimization possible. What don't I see that makes it difficult ?

As far as I understand your optimization goal, there is a way for that:
(defmacro 1+ (x) '(+ 1 ,x))
Since newlisp is fully interpreted language, you may simply to write a preprocessor like this one (the test-and-transform function for +++ is hardcoded for code simlifying, but it can be represented by some complex list-processing function):

(define (compile-proc _wlst)
  (let (_rlst (if (lambda? _wlst) '(lambda) (list? _wlst) '() true))
    (if _rlst _wlst
      (begin
        (if (starts-with _wlst '+++)
          (begin (pop _wlst) (set '_wlst (append '(+ 1) _wlst))))
        (dolist (_witem _wlst)
          (push (compile-proc _witem) _rlst -1))
        _rlst))))
(define (ptest x) (+ 2 (+++ x)))
(define-macro (compile f)
  (set f (compile-proc (eval f))))


> ptest
(lambda (MAIN:x) (+ 2 (MAIN:+++ MAIN:x)))
> (compile ptest)
(lambda (MAIN:x) (+ 2 (+ 1 MAIN:x)))
> ptest
(lambda (MAIN:x) (+ 2 (+ 1 MAIN:x)))
> (ptest 1)
4

In the other hand, newlisp is interpreted language and is mainly focused on the rapid development, easy integration etc. The calculation speed is more about a processor's cost here ;-)

Nevertheless, it'is pretty fast...
WBR, Dmi