

; Trying to answer some criticals on Newlisp in Usenet newsgroupRelated post: On Macro Expansion, Evaluation and Generated Code.
; comp.lang.lisp I was challenged to "show some code" and demonstrate
; my claims that Newlisp macros have some advantages over CL macros on
; examples.
;
; I defined macro at-least, generalized version of the operator or.
; Macro call (at-least expr1 ... exprn) should return true if
; expr2 ... exprn evaluate to true - at least expr1 times. As a special
; case, (at-least 0 ...) should always return true. This is example
; of how code that tests at-least could look alike.
;; Both Newlisp and Common Lisp
(let ((x 1) (y 2) (z 3) (n 3))
(print (at-least n
(at-least (- n 1) (= x 7) (= y 2) (= z 3))
(at-least (- n n) nil nil nil nil)
(at-least (* 1 z) 1 (= 2 2) (let ((z 100))
(= z 1000))))))
; The result of this test is nil, and if 1000 is replaced with 100, true.
; My Newlisp definition was:
;;Newlisp
(define-macro (at-least atn)
(let ((aten (eval atn)))
(doargs(ati (zero? aten))
(when (eval ati)
(dec aten)))
(zero? aten)))
; The best definition Common Lispers managed to do appears to be
; Raffael Cavallaro's definition:
;;Common Lisp
(defmacro at-least (n &rest es &aux (nsym (gensym)) (carsym (gensym)))
(if (null es) nil
`(let ((,nsym ,n))
(if (zerop ,nsym) t
(let ((,carsym ,(car es)))
(if (= ,nsym 1) (or ,carsym ,@`,(cdr es))
(at-least (if ,carsym (1- ,nsym) ,nsym) ,@`,(cdr es))))))))
; How that CL definition compares with my Newlisp definition?
;
; The first and most important, CL macro is not the first-class
; object, so Cavallaro cannot, for example, map or apply his at-least,
; pass it as an argument, return from the function (or other macro) etc.
; This cannot be improved in Common Lisp, macros are not
; the first class citizens.
;
; Other, less important problem with Cavallaro's definition is that
; it requires quadratic macroexpansion time. It can be reduced to
; linear, and in some, special situations it can be the problem.
;
; To be fair, CL definition is safer, i.e. it is harder to accidentally
; shoot oneself in the foot. For programs small enough, it is not
; the problem. But, it might become the problem if program grows
; significantly. However, this problem can be routinely fixed in
; Newlisp - by use of the Newlisp lexical scope features named
; contexts or applying techniques I described on this blog. The contexts
; are general mechanism designed to prevent accidental name clashes
; not reserved for macros, but also functions, variables - all symbols.
; It this case, applying naming convention once code is written
; and tested solves the problem completely. For example,
;;Newlisp
(define-macro (at-least at-least_n)
(let ((at-least_en (eval at-least_n)))
(doargs(at-least_i (zero? at-least_en))
(when (eval at-least_i)
(dec at-least_en)))
(zero? at-least_en)))
; is equally "safe" code as CL macro. Newlispers, however, typically
; use contexts for that purpose.
;
; One of the problems with Common Lisp macros is their complexity.
; Cavallaro is obviously good programmer. His macro has 57 tokens
; vs my 18 tokens - so it is 3.5 times longer. Relatively advanced
; features, like gensym and list splicing (,@) are used. I can
; safely say, according to my experience, that not many people are
; able - or motivated - to write such code.
; Rainer Joswig wrote shorter macro that should be slightly
; changed to return (at-least 0 ...) to be always true. To be fair,
; he wrote it before I specifically mentioned that (at-least 0 ...)
; should always be true (although it is logical, isn't it?)
; Common Lisp
(defmacro at-least (n &rest es &aux (c (gensym)))
`(let ((,c 0))
(or ,@(loop for e in es collect `(and ,e (= (incf ,c) ,n))))))
; Still, my Newlisp macro has some advantages:
;
; * Joswig's CL macro has 31 token, Newlisp macro has 18 tokens.
;
; * Joswig's CL macro has 11 nesting levels, Newlisp macro has 5.
;
; * Joswig's CL macro uses some advanced features like backquote and
; macro LOOP with irregular syntax. Newlisp macro is just one
; loop - there is nothing remotedly advanced here.
;
; * Finally, Joswig's macro is not the first class citizen
; while Newlisp macro is.
;
; However, is there any reason one might prefer CL over Newlisp macros?
;
; Yes, there is. If use of at-least is simple, i.e. at-least is
; mentioned only in expressions like in my test, but never anything
; like (map at-least ...), (apply at-least ...), (eval (at-least ...))
; - CL macro allows compilation and compiled code can run significantly
; faster. This is the main reason Common Lispers avoid eval. Newlisp
; code presented here, even if, theoretically, compiled (Newlisp
; actually has no compiler) wouldn't run that fast. However, if code
; contains the expressions like those mentioned above then Newlisp
; version is either only possible, or it allows significantly faster
; evaluation, because macroexpansion can be avoided - In Common Lisp,
; macroexpansion during runtime is very slow.
;
; As conclusion, I'll cite Alexander Burger, the author of Pico Lisp
; who wrote in 2004:
;
"The Lisp community seems to suffer from a paranoia of “unefficient”
Lisp. This is probably due to the fact that for decades they had
to defend their language against claims like “Lisp is slow” and
“Lisp is bloated”. Partly, this used to be true. But on today’s
hardware raw execution speed doesn’t matter for many practical
applications. "