Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Fexprs more flexible, powerful, easier to learn? (Newlisp vs CL)

496 views
Skip to first unread message

Majorinc Kazimir

unread,
Jan 23, 2009, 3:32:13 PM1/23/09
to
;============================

Pascal:

But where is the paper that argues that fexprs are sometimes
better than macros because they are more flexible and
powerful than macros? Or easier to use, learn, etc? It sounds
like Mr. Majorinc is confident enough that he should be
able to set about writing that paper. I'm looking forward to it!


;============================

I define macro (at-least-two arg1 ... argn)
that returns true if and only if at least
two of args evaluate to the true. Macro is
needed because we want lazy evaluation
of arg1 to argn, like in OR or AND.

Newlisp (17 tokens, very simple)

(define-macro (at-least-two)
(let (c)
(doargs (i (= c 2))
(if (eval i)
(inc c)))
(>= c 2)))

; test

(println (at-least-two (= 1 1)
(= 3 2)
(= 2 5)
(= 2 20)))

(exit)

(A) Could you write simpler, more elegant one in
your favorite Lisp?

Only solution so far is Rainer Joswig's CL

(defmacro at-least-two (&rest expressions)
(let ((c (gensym)))
`(let ((,c 0))
(block nil
,@(loop for expression in expressions
collect `(when ,expression (incf ,c))
collect `(when (= ,c 2) (return t)))
nil))))

It has 38 tokens, and I think it is significantly more
complicated (and not the first class.)

------------------------------------------------

(B) Fexprs are frequently described as "dangerous."
Practice of Newlispers seems to be different:
there are no complains. Probably because Newlispers
typically use lexical / static scope constructs
named "contexts."

However, even without lexical scope, I believe
that fexprs are pretty safe.

(B1) Is anyone able to find the expression that
breaks at-least-two macro?

(B2) Is anyone able to find the expression that
breaks at-least-two macro after trivial name
mangling, for example, replacing i with
at-least-two-i.

(B3) Is anyone able to find the expression that
breaks at-least-two macro after single
application of my function protect2 from
my Newlisp library www.instprog.com,
explained at my blog kazimirmajorinc.blogspot.com,
post "Don't fear Dynamic Scope (2)"

------------------------------------------------
(C) I define more general macro at-least that should
accept expressions of the following kind:

(at-least 3 (= 1 1) (= 2 2) (= 4 4))
(at-least 7 (= 1 1) (= 7 2) ... )

Solution in Newlisp

(define-macro (at-least n)
(let (c)
(doargs (i (= c n))
(if (eval i)
(inc c)))
(>= c n)))

Could you write simpler, more elegant one in
your favorite Lisp?


Rainer Joswig

unread,
Jan 23, 2009, 5:02:54 PM1/23/09
to
In article <gld9gr$m04$1...@ss408.t-com.hr>,
Majorinc Kazimir <fa...@email.address> wrote:

> ;============================
>
> Pascal:
>
> But where is the paper that argues that fexprs are sometimes
> better than macros because they are more flexible and
> powerful than macros? Or easier to use, learn, etc? It sounds
> like Mr. Majorinc is confident enough that he should be
> able to set about writing that paper. I'm looking forward to it!
>
>
> ;============================
>
> I define macro (at-least-two arg1 ... argn)
> that returns true if and only if at least
> two of args evaluate to the true. Macro is
> needed because we want lazy evaluation
> of arg1 to argn, like in OR or AND.
>
> Newlisp (17 tokens, very simple)
>
> (define-macro (at-least-two)
> (let (c)
> (doargs (i (= c 2))
> (if (eval i)
> (inc c)))
> (>= c 2)))


Even if Newlisp names it a macro, it is not a macro.
It is a FEXPR. A Macro would do source transformation.
Above does not do any source transformation.

From Wikipedia, Macro

A macro in computer science is a rule or pattern that
specifies how a certain input sequence (often a
sequence of characters) should be mapped to an
output sequence (also often a sequence of characters)
according to a defined procedure. The mapping process
which instantiates a macro into a specific output
sequence is known as macro expansion.

Your code does not map an input sequence to an
output sequence, thus it is no macro and the
form should be named DEFINE-FEXPR.


>
> ; test
>
> (println (at-least-two (= 1 1)
> (= 3 2)
> (= 2 5)
> (= 2 20)))
>
> (exit)
>
> (A) Could you write simpler, more elegant one in
> your favorite Lisp?
>
> Only solution so far is Rainer Joswig's CL
>
> (defmacro at-least-two (&rest expressions)
> (let ((c (gensym)))
> `(let ((,c 0))
> (block nil
> ,@(loop for expression in expressions
> collect `(when ,expression (incf ,c))
> collect `(when (= ,c 2) (return t)))
> nil))))
>
> It has 38 tokens, and I think it is significantly more
> complicated (and not the first class.)

The 'first class'?

You are a token counter? Here are slightly less tokens
for your more general version.

(defmacro at-least (n &rest es &aux (c (gensym)))
`(let ((,c 0))
(or ,@(loop for e in es collect `(and ,e (= (incf ,c) ,n))))))

Again your FEXPR fails to show the generated code,
because it can't.

Common Lisp can show me the generated code:

CL-USER 91 > (pprint (macroexpand-1 '(at-least 2 (= 1 1) (= 3 2) (= 2 2) (= 4 4))))

(LET ((#:G14745 0))
(OR (AND (= 1 1) (= (INCF #:G14745) 2))
(AND (= 3 2) (= (INCF #:G14745) 2))
(AND (= 2 2) (= (INCF #:G14745) 2))
(AND (= 4 4) (= (INCF #:G14745) 2))))

Well, your version iterates over all forms, counts the true values
and then returns if the sum is equal or greater to 2. So, it does NOT
stop when 2 is already reached.

Let me change my version so that it does the same:

(defmacro at-least (n &rest es &aux (c (gensym)))
`(let ((,c 0))
,@(loop for e in es collect `(when ,e (incf ,c)))
(>= ,c ,n)))


CL-USER 98 > (pprint (macroexpand-1 '(at-least 2 (= 1 1) (= 3 2) (= 2 2) (= 4 4))))

(LET ((#:G14786 0))
(WHEN (= 1 1) (INCF #:G14786))
(WHEN (= 3 2) (INCF #:G14786))
(WHEN (= 2 2) (INCF #:G14786))
(WHEN (= 4 4) (INCF #:G14786))
(>= #:G14786 2))

The Newlisp manual also seems to have a wrong example for lambda-macro:

http://www.newlisp.org/newlisp_manual.html

(lambda-macro (a b) (set (eval a) b)) evaluates to (lambda (x) (* x x))

That would surprise me...

--
http://lispm.dyndns.org/

Kaz Kylheku

unread,
Jan 23, 2009, 5:24:09 PM1/23/09
to
On 2009-01-23, Majorinc Kazimir <fa...@email.address> wrote:
> ;============================
>
> Pascal:
>
> But where is the paper that argues that fexprs are sometimes
> better than macros because they are more flexible and
> powerful than macros? Or easier to use, learn, etc? It sounds
> like Mr. Majorinc is confident enough that he should be
> able to set about writing that paper. I'm looking forward to it!

Pascal didn't write that.

> Newlisp (17 tokens, very simple)

Well, we have to give him credit for counting tokens.
J. Harrop counts characters:

[He can bore with Eff Sharp.
He can snooze with Oh Camel.
He measures code size
at the character level.]

So, token count translates to flexiblity and power. Hmm!

> (define-macro (at-least-two)
> (let (c)

You didn't initialize c to zero, and you're incrementing it. You mean nil is
zero in newLISP? Or uninitialized variables are 0 instead of nil?

> (doargs (i (= c 2))

So trailing args are implicit, like "$@" in the Bourne shell.

> (if (eval i)
> (inc c)))


> (>= c 2)))

Why the double evaluation of the condition with respect to C?

(defun at-least-n (n-func &rest funcs)
(loop with limit = (funcall n-func)
with c = 0
for f in funcs
when (funcall f) do (incf c)
when (>= c limit) do (return t)))

(defmacro lambda-call (function &rest exprs)
`(funcall ,function ,@(mapcar (lambda (expr) `(lambda nil ,expr)) exprs)))

(lambda-call 'at-least-n 3 (print 'a) (print 'b) nil (print 'c) (print 'd))

-> T

output:
A
B
C

Though LAMBDA-CALL is a special form, AT-LEAST-N is a first class function that
takes functional arguments:

(at-least-n (lambda () 3) (lambda () (print 'a)) ...)

Lambdas are compiled when the program in which they are contained is compiled,
and give you access to lexical variables. E.g.:

(let ((foo 42))
(lambda-call #'funcall (incf foo))
foo))

Your own ``first class'' while loop:

(defun while (guard &rest body)
(loop while (funcall guard)
do (mapcar #'funcall body)))

(let ((x 0))
(lambda-call #'while (< x 10) (print x) (incf x)))

-> NIL

Output:
0
1
2
3
4
5
6
7
8
9

There is no need to have functions that take unevaluated arguments, because you
can make a special operator like LAMBDA-CALL that gives you syntactic sugar
into turning forms into the bodies of simple functions, and passing the
resulting functions to a function. The function then uses FUNCALL to
``evaluate'' these compiled bodies rather than EVAL. (Above, the function /is/
FUNCALL).

Though lambda-call isn't first class, it doesn't matter because it is one of a
kind. You would be indirecting upon different functions, not upon different
operators similar to LAMBDA-CALL.

> (B) Fexprs are frequently described as "dangerous."
> Practice of Newlispers seems to be different:
> there are no complains.

You could dunk a newlisper into a vat of shit up to just below the level of his
nostrils and he wouldn't dare move, or open his mouth to complain. :)

> (B1) Is anyone able to find the expression that
> breaks at-least-two macro?

You did, apparently, see below:

> (B3) Is anyone able to find the expression that
> breaks at-least-two macro after single
> application of my function protect2 from

Otherwise why would you need protect2. Is there going to be a protect3
to protect protect2? ;)

Kaz Kylheku

unread,
Jan 23, 2009, 5:46:00 PM1/23/09
to
On 2009-01-23, Rainer Joswig <jos...@lisp.de> wrote:
> In article <gld9gr$m04$1...@ss408.t-com.hr>,
> Majorinc Kazimir <fa...@email.address> wrote:
>> (define-macro (at-least-two)
>> (let (c)
>> (doargs (i (= c 2))
>> (if (eval i)
>> (inc c)))
>> (>= c 2)))
> Well, your version iterates over all forms, counts the true values
> and then returns if the sum is equal or greater to 2. So, it does NOT
> stop when 2 is already reached.

I thought that for a second too, but it looks as if the DOARGS form used in the
newLISP ``macro'' definition takes the second form as an extra guard:

(doargs (i (= i 2) ...)
(>= i 2)

I.e. `(doargs (,var ,until-expr) ...). So DOARGS has a double condition for
terminating: implicitly running out of args, or the expression becoming true.

The code is slightly confusing because he it repeats the test again. After
the execution of the loop, it's not clear whether it terminated because it ran
out of arguments, or because i reached the maximum value.

It's like those silly C programs that do:

for (i = 0; i < N; i++) {
/* ... */
if (found)
break;
}

if (i == N) {
/* ooops not found */
}

:)

Dimiter "malkia" Stanev

unread,
Jan 23, 2009, 5:59:57 PM1/23/09
to
> It's like those silly C programs that do:
>
> for (i = 0; i < N; i++) {
> /* ... */
> if (found)
> break;
> }
>
> if (i == N) {
> /* ooops not found */
> }
>
> :)

Duh... :) - It's.... just.... that today I wrote such code, for upcoming
patch for our game, hehe...

Off course I know that i would be N if nothing was found, right after
the loop, at least in most "C" compilers that would be the case
(compilers that I know, used, etc), but at least for one such compiler
and specific optimization options that wasn't the case - on it, i was
equal to N-1 after the cycle (or N+1, can't remmember) - I think it was
a SH3 CodeWarrior compiler that we used for early Dreamcast dev. But
memory might play tricks on me.

Actually I don't think the "C" standard covers that case, it's
implementation-specific (which means undefined, and UP-TO-YOU!), so
generally it's bad code, and I wrote such code just yesterday (yet I'm
relieved, as much of the codebase uses such style, so if it breaks,
it'll break many places).

Kaz Kylheku

unread,
Jan 23, 2009, 6:38:13 PM1/23/09
to
On 2009-01-23, Dimiter "malkia" Stanev <mal...@mac.com> wrote:
>> It's like those silly C programs that do:
>>
>> for (i = 0; i < N; i++) {
>> /* ... */
>> if (found)
>> break;
>> }
>>
>> if (i == N) {
>> /* ooops not found */
>> }
>>
>> :)
>
> Duh... :) - It's.... just.... that today I wrote such code, for upcoming
> patch for our game, hehe...
>
> Off course I know that i would be N if nothing was found, right after
> the loop, at least in most "C" compilers that would be the case
> (compilers that I know, used, etc), but at least for one such compiler
> and specific optimization options that wasn't the case - on it, i was
> equal to N-1 after the cycle (or N+1, can't remmember) - I think it was
> a SH3 CodeWarrior compiler that we used for early Dreamcast dev.

That's a ridiculous bug. I've had a few run-ins with Metrowerks warez. One one
project I was helping some developers port some our code to a platform using
some CW IDE. The stupid thing insisted on compiling everything with the same
compiler, either C or C++. Global switch! It wouldn't look at the .cc and .c
suffixes of the files. The target was in the mc68K (old PalmOS). The compiler
wouldn't emit larger than 16 bit jumps between functions, so object files
couldn't compile to more than 64K of code. A couple of our large C++ modules
had to be split into separate .cc files. IIRC, I put my foot down about that;
and had it implemented as multiple translation units in one source file with
some #ifdefs and multiple compiler passes, to keep the file intact for the sake
of merges and such.

> Actually I don't think the "C" standard covers that case, it's

When you speak about a programming language standard without having seen
so much as its cover page, sorry, that's not thinking!

Given a loop like for (i = 0; i < N; i++), provided that the loop does not have
some early break, and does not mess with the value of i; and provided that the
value N is not out of the range of the type of i; after the execution of that
loop, i must have the value N! The predicate i == N is one of the basic
post-conditions of the loop, whose body keeps executing and incrementing i
while i < N.

On the last iteration, i is N-1 and the body is executed, and then the
increment step, which brings i to N. The guard is then evaluated (i < N) and
found to be false because i is N; the loop then terminates without doing
anything more to i.

> implementation-specific (which means undefined, and UP-TO-YOU!), so

Undefined does not mean up to you, it means up to your compiler implementors,
or else up to pure luck. Definitely not you, the programmer.

Dimiter "malkia" Stanev

unread,
Jan 23, 2009, 6:49:42 PM1/23/09
to
> When you speak about a programming language standard without having seen
> so much as its cover page, sorry, that's not thinking!

True! We did have some local copy of C++ standard, but even if I read
it, it won't help us much - either the compiler wasn't following
anything, or once you learn it - you'll be always having the feeling
being a prick about why the compiler is stupid enough not to fix it.
There are even C/C++ compilers that claim to be 100% ANSI C, but then
again they either are not available, too obscure, or do not generate
optimized code.

I've actually stopped liking "C", much more because of "C++" and the
pressure it puts on people trying to grok it fully. (In fact I love "C",
but at work, I'm more or less forced to use "C++" and a "C++" without
exceptions and RTTI in the runtime (though we could use the latter only
for tools).

> Given a loop like for (i = 0; i < N; i++), provided that the loop does not have
> some early break, and does not mess with the value of i; and provided that the
> value N is not out of the range of the type of i; after the execution of that
> loop, i must have the value N! The predicate i == N is one of the basic
> post-conditions of the loop, whose body keeps executing and incrementing i
> while i < N.

If that's in the standard, then I guess I was blindly coding the correct
stuff. Is there any reference online, where I can verify that (I'm
simply curious).

> On the last iteration, i is N-1 and the body is executed, and then the
> increment step, which brings i to N. The guard is then evaluated (i < N) and
> found to be false because i is N; the loop then terminates without doing
> anything more to i.
>
>> implementation-specific (which means undefined, and UP-TO-YOU!), so

That was really a joke (UP-TO-YOU) - it simply meant - do whatever you
need to do, just make this code work for the platforms you are targeting
(in our cases - that's mostly game consoles and PC).

> Undefined does not mean up to you, it means up to your compiler implementors,
> or else up to pure luck. Definitely not you, the programmer.

Yup.

Btw, I've learned quite a lot from your latest posts!

Cheers!

jos...@corporate-world.lisp.de

unread,
Jan 23, 2009, 7:00:09 PM1/23/09
to
On Jan 23, 11:46 pm, Kaz Kylheku <kkylh...@gmail.com> wrote:
> On 2009-01-23, Rainer Joswig <jos...@lisp.de> wrote:
>
> > In article <gld9gr$m0...@ss408.t-com.hr>,

> >  Majorinc Kazimir <fa...@email.address> wrote:
> >>      (define-macro (at-least-two)
> >>               (let (c)
> >>                    (doargs (i (= c 2))
> >>                            (if (eval i)
> >>                                (inc c)))
> >>                    (>= c 2)))
> > Well, your version iterates over all forms, counts the true values
> > and then returns if the sum is equal or greater to 2. So, it does NOT
> > stop when 2 is already reached.
>
> I thought that for a second too, but it looks as if the DOARGS form used in the
> newLISP ``macro'' definition takes the second form as an extra guard:
>
>   (doargs (i (= i 2) ...)
>   (>= i 2)

Ah, I see. Also, let me guess: if a form e1 of (atleast-two e1 e2 ...)
sets c to 1 and evaluates
to true, then it terminates already? Also: if e1 sets c to some non-
number and evaluates to true
then (inc c) fails.

André Thieme

unread,
Jan 23, 2009, 7:02:05 PM1/23/09
to
Majorinc Kazimir schrieb:

> ;============================
>
> Pascal:
>
> But where is the paper that argues that fexprs are sometimes
> better than macros because they are more flexible and
> powerful than macros? Or easier to use, learn, etc? It sounds
> like Mr. Majorinc is confident enough that he should be
> able to set about writing that paper. I'm looking forward to it!
>
>
> ;============================
>
> I define macro (at-least-two arg1 ... argn)
> that returns true if and only if at least
> two of args evaluate to the true. Macro is
> needed because we want lazy evaluation
> of arg1 to argn, like in OR or AND.
>
> Newlisp (17 tokens, very simple)
>
> (define-macro (at-least-two)
> (let (c)
> (doargs (i (= c 2))
> (if (eval i)
> (inc c)))
> (>= c 2)))

It looks not very complicated, but I wonder what
“(let (c)” means.
Is this in CL like “(let ((c 0))”?

Will your code eval the first two args in any case?
Even if only one is given? It seems to me that the (eval i)
will take place in the case when at-least-two was called with
exactly one argument.

Here I have a version in Clojure. It will return nil if less than
two args were given. In the other case it will evaluate the first
two arguments:

(defmacro at-least-two [& r]
(let [c (take 2 r)]
(when (= 2 (count c))
`(do ~@c))))

This version is even better than yours in my opinion.
It also has 17 tokens. Your version needs 150% of the lines of code of
my version, and my version is also shorter in character count:
user> (count "(define-macro (at-least-two)


(let (c)
(doargs (i (= c 2))
(if (eval i)
(inc c)))

(>= c 2)))")
91

vs

user> (count "(defmacro at-least-two [& r]
(let [c (take 2 r)]
(when (= 2 (count c))
`(do ~@c))))")
83

As you see, I did not count the leading spaces. So, your version does
not fall too far behind.
So, your version has nearly 10% more chars.
But the best feature about my version is, in my opinion of course,
that it is dramatically much more readable.
There are no side effects. No counter c is increased.
There is no need for a loop. I find it very clear.
Take up to 2 args out of the given args. When 2 args were taken, then
compile it into code (that’s the “do”).
Perhaps you will agree with me. But I am open to hear that your opinion
is different, and that you find your version easier and more readable.


> (C) I define more general macro at-least that should
> accept expressions of the following kind:
>
> (at-least 3 (= 1 1) (= 2 2) (= 4 4))
> (at-least 7 (= 1 1) (= 7 2) ... )
>
> Solution in Newlisp
>
> (define-macro (at-least n)
> (let (c)
> (doargs (i (= c n))
> (if (eval i)
> (inc c)))
> (>= c n)))
>
> Could you write simpler, more elegant one in
> your favorite Lisp?

This is of course very objective.
Although I think lots of programmers would agree that it will
be easier to understand if the side effect of incrementing a not
explicitly initialized counter and the loop could be eliminated.
So I would just do the same as you:

(defmacro at-least-two [n & r]
(let [c (take n r)]
(when (= n (count c))
`(do ~@c))))

Again, I find my version simpler. Not much.
But, even for this short code, far more elegant.


André
--
Lisp is not dead. It’s just the URL that has changed:
http://clojure.org/

Majorinc Kazimir

unread,
Jan 23, 2009, 7:17:13 PM1/23/09
to

> Well, your version iterates over all forms, counts the true values
> and then returns if the sum is equal or greater to 2. So, it does NOT
> stop when 2 is already reached.

You somehow made mistake. It does stop. For debugging
purpose I added one print.
-------------------
CODE:

(define-macro (at-least n)
(let (c)
(doargs (i (= c n))
(print i) ;;;;;; This one


(if (eval i)
(inc c)))

(>= c n)))

(at-least 3 (= 1 1) (= 2 2) (= 3 3) (= 4 4) (= 5 5))
(exit)

----
OUTPUT:

(= 1 1)(= 2 2)(= 3 3)true
-----------------
It works.


> Even if Newlisp names it a macro, it is not a macro.
> It is a FEXPR. A Macro would do source transformation.
> Above does not do any source transformation.

I agree. As it is terminology issue, I decided to stay
with version that is used in two languages.


> (defmacro at-least (n &rest es &aux (c (gensym)))
> `(let ((,c 0))
> (or ,@(loop for e in es collect `(and ,e (= (incf ,c) ,n))))))
>
> Again your FEXPR fails to show the generated code,
> because it can't.

True, Newlisp macros (Lisp fexpr's) do not
necessarily generate code. But they COULD generate
the code if you really want it that way:

; Newlisp macro written in the Common Lisp style
(define-macro (at-least n)
(println (append '(let (c))
(list
(cons 'or
(map (lambda(x)
(expand '(and x (inc c) (= c n))
'x))
(args)))))))

This is fexpr that does exactly the same thing as your macro.
It generates code and prints it - if you want that it evaluates it,
just replace println with eval. It has 27 tokens. It is not natural to
force generating whole code in fexprs, but it is possible.

It was my original one:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-macro (at-least n)
(let (c)
(doargs (i (= c n))


(if (eval i)
(inc c)))

(>= c n)))


As I myself wrote it, do you believe me that it was
easier?


> (lambda-macro (a b) (set (eval a) b)) evaluates to (lambda (x) (* x x))
>

Yes, it is mistake. I'll report it, thanks.
In essence, it evaluates to

jos...@corporate-world.lisp.de

unread,
Jan 23, 2009, 7:24:54 PM1/23/09
to
On Jan 24, 1:02 am, André Thieme <address.good.until.


The task, as I understand it, was not to check if there are two args
and evaluate those.
The task is to evaluate the all args until at least two have been
evaluated to true.

>
> This version is even better than yours in my opinion.
> It also has 17 tokens. Your version needs 150% of the lines of code of
> my version, and my version is also shorter in character count:

a character counter

...

Kaz Kylheku

unread,
Jan 23, 2009, 7:30:40 PM1/23/09
to
On 2009-01-24, André Thieme <address.good.un...@justmail.de> wrote:
> (defmacro at-least-two [& r]
> (let [c (take 2 r)]
> (when (= 2 (count c))
> `(do ~@c))))

That's not what his ``macro'' (frexp) does. It keeps evaluating
expressions left to right until it runs out, or until it has encountered
two that are true. It then stops evaluating, and returns true if two had been
found true, otherwise false.

So you cannot simply take two expressions and ignore the rest.

Your macro also can't just not emit any code at all when there are fewer than
two expressions. The expressions may have side effects. If there is only one
expression, you know that the result will have to be false, but the effect
of that expression still has to take place.

> This version is even better than yours in my opinion.

But it's not first class like a frexp. :)

> As you see, I did not count the leading spaces.

In general, a fairly good comparison for size of Lisp code is the number of
atoms in the tree structure of the forms (say, including ()/NIL). (a (b c) d)
has the same size as (a b c d). Same atom count, same cons count, different
tree balance. (There are obvious ways to extend this to other languages; give
them a very charitable, if informal, s-exp conversion and use that).

jos...@corporate-world.lisp.de

unread,
Jan 23, 2009, 7:34:02 PM1/23/09
to
On Jan 24, 1:17 am, Majorinc Kazimir <fa...@email.address> wrote:
>  > Well, your version iterates over all forms, counts the true values
>  > and then returns if the sum is equal or greater to 2. So, it does NOT
>  > stop when 2 is already reached.
>
> You somehow made mistake. It does stop. For debugging
> purpose I added one print.

I did not look to close at DOARGS, my fault.

> > Even if Newlisp names it a macro, it is not a macro.
> > It is a FEXPR. A Macro would do source transformation.
> > Above does not do any source transformation.
>
> I agree. As it is terminology issue, I decided to stay
> with version that is used in two languages.

Like Newlisp is mostly not a new Lisp.

> True, Newlisp macros (Lisp fexpr's) do not
> necessarily generate code. But they COULD generate
> the code if you really want it that way:
>
> ; Newlisp macro written in the Common Lisp style
> (define-macro (at-least n)
>    (println (append '(let (c))
>                     (list
>                        (cons 'or
>                          (map (lambda(x)
>                                 (expand '(and x (inc c) (= c n))
>                                         'x))
>                               (args)))))))
>
> This is fexpr that does exactly the same thing as your macro.
> It generates code and prints it - if you want that it evaluates it,
> just replace println with eval. It has 27 tokens. It is not natural to
> force generating whole code in fexprs, but it is possible.

No, doubt, you can not only morph code into data and evaluate the
data,
you can also morph code into data, generate more data out of that and
evaluate that data. Common Lisp shows that none of that
is necessary.


> It was my original one:
>
> ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> (define-macro (at-least n)
>           (let (c)
>                (doargs (i (= c n))
>                        (if (eval i)
>                            (inc c)))
>                (>= c n)))
>
> As I myself wrote it, do you believe me that it was
> easier?

It is not that much how easy it is, it is about how horrible it will
be to debug any non-trivial amount of code using FEXPRs.

...

Majorinc Kazimir

unread,
Jan 23, 2009, 7:47:34 PM1/23/09
to

>
> Ah, I see. Also, let me guess: if a form e1 of (atleast-two e1 e2 ...)
> sets c to 1 and evaluates
> to true, then it terminates already? Also: if e1 sets c to some non-
> number and evaluates to true
> then (inc c) fails.

Right. That was (B1).

>
>> I.e. `(doargs (,var ,until-expr) ...). So DOARGS has a double condition for
>> terminating: implicitly running out of args, or the expression becoming true.
>>
>> The code is slightly confusing because he it repeats the test again. After
>> the execution of the loop, it's not clear whether it terminated because it ran
>> out of arguments, or because i reached the maximum value.

Yes.

jos...@corporate-world.lisp.de

unread,
Jan 23, 2009, 8:12:13 PM1/23/09
to
On Jan 24, 1:47 am, Majorinc Kazimir <fa...@email.address> wrote:
> > Ah, I see. Also, let me guess: if a form e1 of (atleast-two e1 e2 ...)
> > sets c to 1 and evaluates
> > to true, then it terminates already? Also: if e1 sets c to some non-
> > number and evaluates to true
> > then (inc c) fails.
>
> Right. That was (B1).

My Common Lisp version does not have that problem. Maybe you want to
your increase token count a bit and show a safer version?

Let me guess: (let ((i 'something)) (at-least-two (eval i))) also
does not work.
doargs sets i to the list (eval i), and then evaluates it... which
does what?

André Thieme

unread,
Jan 23, 2009, 8:13:59 PM1/23/09
to
Thanks to Rainer and Kaz for explanations.
Maybe this one is right?
Anyway, the fact that I didn’t understood (and still don’t understand
it well) the NewLisp code means at least for me it was not easy enough
to read. Perhaps I am just not trained enough in NewLisp.
(and I also did not look at the CL example and I didn’t follow the
discussion before)

(defmacro at-least-two [& x]
`(= 2 (count (take 2 (filter eval '~x)))))

filter will lazily feed eval with the args that were passed to the macro.
Besides that, filter is like CLs remove-if-not.

This macro will eval all args passed to at-least-two until a maximum of
two of these args evaled to true. (that is: not “nil” and not “false”).
As Kaz suggested, now even those args that eval to nil or false will be
run, so side effects take place.

Obviously I find this version even more elegant than the one before.
And it is even shorter.
@Rainer: of course, counting characters is a very idiotic thing. But
it still is enjoyable for me to do it, because I feel a little bit like
“beat ‘em with their own weapons”.
I am not seriously drawing conclusions from these 5 liners. I just do it
for the fun. Also Tamas complained in the past about it. I ask you two
to just let me continue, for special friends.
Mostly for guys like Jillian James :-)


André
--

jos...@corporate-world.lisp.de

unread,
Jan 23, 2009, 8:22:05 PM1/23/09
to
On Jan 24, 2:13 am, André Thieme <address.good.until.

2009.may...@justmail.de> wrote:
> Thanks to Rainer and Kaz for explanations.
> Maybe this one is right?
> Anyway, the fact that I didn’t understood (and still don’t understand
> it well) the NewLisp code means at least for me it was not easy enough
> to read. Perhaps I am just not trained enough in NewLisp.
> (and I also did not look at the CL example and I didn’t follow the
> discussion before)
>
> (defmacro at-least-two [& x]
>    `(= 2 (count (take 2 (filter eval '~x)))))

It takes the first two and counts those?

Shouldn't it count the expressions that evaluate to true over all
items?

Will that see variables?

In CL

(let ((a t) (b nil) (c t))
(at-least-two a b c))

?

André Thieme

unread,
Jan 23, 2009, 8:54:42 PM1/23/09
to
jos...@corporate-world.lisp.de schrieb:

> On Jan 24, 2:13 am, André Thieme <address.good.until.
> 2009.may...@justmail.de> wrote:
>> Thanks to Rainer and Kaz for explanations.
>> Maybe this one is right?
>> Anyway, the fact that I didn’t understood (and still don’t understand
>> it well) the NewLisp code means at least for me it was not easy enough
>> to read. Perhaps I am just not trained enough in NewLisp.
>> (and I also did not look at the CL example and I didn’t follow the
>> discussion before)
>>
>> (defmacro at-least-two [& x]
>> `(= 2 (count (take 2 (filter eval '~x)))))
>
> It takes the first two and counts those?

Filter keeps only those elements which evaled to a different value than
nil or false.
If n args are giving filter can max return a list of n elements, like
remove-if-not in CL. In such a case it is like (mapcar #'identity list).
So, filter will return a list whose length is between 0 and n.
Out of this list take tries to take as many elements as possible, but
max 2. So, take can result in a list of the lengths 0, 1 or 2.


> Shouldn't it count the expressions that evaluate to true over all
> items?

I don’t know. I am not sure what the original poster really wanted.
As I don’t fully understand his code I have to guess.


> Will that see variables?

Good objection.

> In CL
>
> (let ((a t) (b nil) (c t))
> (at-least-two a b c))
>
> ?

No, my version can’t see them.


André
--

jos...@corporate-world.lisp.de

unread,
Jan 23, 2009, 9:02:46 PM1/23/09
to
On Jan 24, 2:54 am, André Thieme <address.good.until.

2009.may...@justmail.de> wrote:
> jos...@corporate-world.lisp.de schrieb:
>
> > On Jan 24, 2:13 am, André Thieme <address.good.until.
> > 2009.may...@justmail.de> wrote:
> >> Thanks to Rainer and Kaz for explanations.
> >> Maybe this one is right?
> >> Anyway, the fact that I didn’t understood (and still don’t understand
> >> it well) the NewLisp code means at least for me it was not easy enough
> >> to read. Perhaps I am just not trained enough in NewLisp.
> >> (and I also did not look at the CL example and I didn’t follow the
> >> discussion before)
>
> >> (defmacro at-least-two [& x]
> >>    `(= 2 (count (take 2 (filter eval '~x)))))
>
> > It takes the first two and counts those?
>
> Filter keeps only those elements which evaled to a different value than
> nil or false.
> If n args are giving filter can max return a list of n elements, like
> remove-if-not in CL. In such a case it is like (mapcar #'identity list).
> So, filter will return a list whose length is between 0 and n.
> Out of this list take tries to take as many elements as possible, but
> max 2. So, take can result in a list of the lengths 0, 1 or 2.

Okay, looks good.

...

> > Will that see variables?
>
> Good objection.
>
> > In CL
>
> > (let ((a t) (b nil) (c t))
> >   (at-least-two a b c))
>
> > ?
>
> No, my version can’t see them.

You may need to increase token and character count...

Majorinc Kazimir

unread,
Jan 23, 2009, 9:34:48 PM1/23/09
to
Newlisp 13 tokens for extended version:

(define-macro (at-least cnt)
(exists (lambda(x)(zero? (and (eval x) (dec cnt)))) $args))

Kaz Kylheku

unread,
Jan 23, 2009, 10:31:24 PM1/23/09
to
On 2009-01-24, jos...@corporate-world.lisp.de <jos...@corporate-world.lisp.de>
wrote:

> On Jan 24, 2:13 am, André Thieme <address.good.until.
> 2009.may...@justmail.de> wrote:
>> Thanks to Rainer and Kaz for explanations.
>> Maybe this one is right?
>> Anyway, the fact that I didn’t understood (and still don’t understand
>> it well) the NewLisp code means at least for me it was not easy enough
>> to read. Perhaps I am just not trained enough in NewLisp.
>> (and I also did not look at the CL example and I didn’t follow the
>> discussion before)
>>
>> (defmacro at-least-two [& x]
>>    `(= 2 (count (take 2 (filter eval '~x)))))
>
> It takes the first two and counts those?
>
> Shouldn't it count the expressions that evaluate to true over all
> items?

Once TAKE 2 pulls out two, it is done. Pardon me, it must be COUNT that
actually pulls out the items. (TAKE 2) does nothing, just produces a virtual
sequence. If you don't use that value, no evaluation happens. But COUNT must
walk the whole sequence until the end, so it kicks TAKE into action, which
produces two items and then is done. In so doing, it must take at least two
items from the virtual sequence produced by the FILTER. FILTER will only
produce as many items as TAKE 2 asks for, i.e. at most two, but in so doing it
may process and filter out many forms that evaluate false. Once it produces two
truths though, it won't evaluate any more.

EVAL meets lazy sequences: a jam between psychedelic acid rock and progressive
metal!

:)

Let's do it in CL:


(defun lazy-list (ordinary-list)
(lambda ()
(if ordinary-list
(pop ordinary-list)
(values nil t))))

(defun lazy-filter (function lazy-list)
(lambda ()
(loop
(multiple-value-bind (next nomore)
(funcall lazy-list)
(cond
(nomore (return (values nil t)))
((funcall function next) (return next)))))))

Test:

[4]> (defparameter f (lazy-filter #'evenp (lazy-list '(1 2 3 4 5))))
F
[5]> (funcall f)
2
[6]> (funcall f)
4
[7]> (funcall f)
NIL ;
T
[8]> (funcall f)
NIL ;
T

(defun lazy-take (num lazy-list)
(lambda ()
(if (plusp (prog1 num (decf num)))
(funcall lazy-list)
(values nil t))))

(defun lazy-count (lazy-list)
(let ((count 0))
(loop
(multiple-value-bind (next nomore)
(funcall lazy-list)
(if nomore
(return count)
(incf count))))))


Now, transliterate the Clojure macro:

(defmacro at-least-two (&rest x)
`(= 2 (lazy-count (lazy-take 2 (lazy-filter #'eval (lazy-list ',x))))))

[9]> (macroexpand '(at-least-two a b c d e))
(= 2 (LAZY-COUNT (LAZY-TAKE 2 (LAZY-FILTER #'EVAL (LAZY-LIST '(A B C D E)))))) ;
T

[10]> (at-least-two (princ 'a) nil nil (print 'b) (print 'c))
AB
T
[11]> (at-least-two (princ 'a) nil nil (print 'c))
AC
T
[12]> (at-least-two (princ 'a) (print 'c))
AC
T
[13]> (at-least-two (princ 'a))
A
NIL

Woiks for me!

> Will that see variables?

and must be ... FIRST ... CLASS!

Good grief. :)

William James

unread,
Jan 23, 2009, 11:19:01 PM1/23/09
to
Majorinc Kazimir wrote:

Ruby:


def at_least n, list
list.any?{|x| eval x and 0 == n -= 1}
end

at_least 3, %w(1==1 2==2 3==3 4==4 5==5)

jos...@corporate-world.lisp.de

unread,
Jan 24, 2009, 3:15:28 AM1/24/09
to

Still with the name capture problems.

André Thieme

unread,
Jan 24, 2009, 9:11:05 AM1/24/09
to
jos...@corporate-world.lisp.de schrieb:

>>> In CL
>>> (let ((a t) (b nil) (c t))
>>> (at-least-two a b c))
>>> ?
>> No, my version can’t see them.
>
> You may need to increase token and character count...

Yes, you are right.
I was really too tired, did not get much sleep this week.
Okay, so here we go:

(defmacro at-least-two [& x]
`(= 2 (count (take 2 (filter eval [~@x])))))

The change is what follows the “eval”.
It was: '~x
And now is: [~@x]

Explanation:
x will be a list of all given args in the macro.
Inside a backquote expression x is just the symbol x.
To unquote it one uses “~” in Clojure, as “,” is just
whitespace (for readability really nice, IMO).
~x would place the list of all given args behind the eval.
In the case of (at-least-two a b c) this would be
(a b c)
But (filter eval (a b c)) does not make much sense typically,
because the function a is not defined.
So, a call like:
(let [a nil, b nil, c false, d true] (at-least-two a b c d))
would result in a NullPointerException.

In my original version I did not think about variable capture,
so a call like:
(at-least-two 5 6 7)
would result in (filter eval (5 6 7)) which also makes no sense,
as Clojure can’t call 5.

That’s why I quoted the ~x in my macro: '~x
Now (at-least-two 5 6 7) results in a (filter eval '(5 6 7)).
This however can’t work if variables are in the game, because those
would be treated literally, as symbols.

If Clojure would put the rest args into a vector instead of a list,
then ~x would have been okay. But as this does not happen I say:
[~x] which would be ==> [(5 6 7)]
But [~@x] ==> [5 6 7]
Now it is not quoted anymore, and also variables are treated properly:

user> (let [a nil, b nil, c false, d true] (at-least-two a b (println 5) d))
5
false

Note that the false is the return value of the call to at-least-two.
The 5 directly above it is what was printed. Note that println just
prints and returns nil. So, unlike CLs print it won’t return 5.


(let [a nil, b nil, c false, d true] (at-least-two b a d c d))
==> true

André Thieme

unread,
Jan 24, 2009, 9:15:29 AM1/24/09
to
Kaz Kylheku schrieb:

> On 2009-01-24, jos...@corporate-world.lisp.de <jos...@corporate-world.lisp.de>
> wrote:
>> On Jan 24, 2:13 am, André Thieme <address.good.until.
>> 2009.may...@justmail.de> wrote:

>>> (defmacro at-least-two [& x]
>>> `(= 2 (count (take 2 (filter eval '~x)))))
>> It takes the first two and counts those?
>>
>> Shouldn't it count the expressions that evaluate to true over all
>> items?
>
> Once TAKE 2 pulls out two, it is done. Pardon me, it must be COUNT that
> actually pulls out the items.

Yes, you are absolutely right. Did you know that because you already
worked in Clojure? Or was this the result of your analysis?


> (TAKE 2) does nothing, just produces a virtual sequence.

Exactly.


> If you don't use that value, no evaluation happens. But COUNT must
> walk the whole sequence until the end, so it kicks TAKE into action, which
> produces two items and then is done. In so doing, it must take at least two
> items from the virtual sequence produced by the FILTER. FILTER will only
> produce as many items as TAKE 2 asks for, i.e. at most two, but in so doing it
> may process and filter out many forms that evaluate false. Once it produces two
> truths though, it won't evaluate any more.

Perfect explanation.
And thanks for your translation into CL.


André
--

André Thieme

unread,
Jan 24, 2009, 10:59:05 AM1/24/09
to
André Thieme schrieb:

> (defmacro at-least-two [& x]
> `(= 2 (count (take 2 (filter eval [~@x])))))
>

André, André, André...
Your version evals everything.
You could make it a function please.
Then you would end up with something like what Jillian James offered.

(defmacro at-least-two [& x]
`(= 2 (count (take 2 (filter identity (lazy-cat ~@(map vector x)))))))


Now
(let [a nil, b false, c true]
(at-least-two a b c (= 5 6) (println 7) (println 8)))
will print 7 and 8 and ==> false

And
(let [a nil, b false, c true]
(at-least-two a b c (= 5 6) (println 7) c (println 8)))
will print 7 and ==> true

Leandro Rios

unread,
Jan 24, 2009, 11:19:23 AM1/24/09
to
jos...@corporate-world.lisp.de escribió:

> On Jan 24, 2:54 am, André Thieme <address.good.until.
> 2009.may...@justmail.de> wrote:
>> jos...@corporate-world.lisp.de schrieb:
>>
>>> On Jan 24, 2:13 am, André Thieme <address.good.until.
>>> 2009.may...@justmail.de> wrote:
>>>> Thanks to Rainer and Kaz for explanations.
>>>> Maybe this one is right?
>>>> Anyway, the fact that I didn’t understood (and still don’t understand
>>>> it well) the NewLisp code means at least for me it was not easy enough
>>>> to read. Perhaps I am just not trained enough in NewLisp.
>>>> (and I also did not look at the CL example and I didn’t follow the
>>>> discussion before)
>>>> (defmacro at-least-two [& x]
>>>> `(= 2 (count (take 2 (filter eval '~x)))))
>>> It takes the first two and counts those?
>> Filter keeps only those elements which evaled to a different value than
>> nil or false.
>> If n args are giving filter can max return a list of n elements, like
>> remove-if-not in CL. In such a case it is like (mapcar #'identity list).
>> So, filter will return a list whose length is between 0 and n.
>> Out of this list take tries to take as many elements as possible, but
>> max 2. So, take can result in a list of the lengths 0, 1 or 2.
>
> Okay, looks good.

Isn't filter evaluating all of its arguments when at-least-two should
stop when (precisely) at least two of them evaluated to true? And why is
take necessary? Wouldn't it be the same to say

(defmacro at-least-two [& x]
`(> 1 (count (filter eval '~x)))) ?

Don't forget: We are counting chars! :)

Disclaimer:I didn't look too closely to Clojure, so it's very possible
that I'm wrong.

Leandro

André Thieme

unread,
Jan 24, 2009, 11:19:53 AM1/24/09
to
William James schrieb:

That’s not bad Jill!
Although you will need some more adoption.
Your function takes a list/an array of args, although it should take any
number of args instead, as in:
at_least(2, 1==1, 1==2, puts(3), 2==2, puts(4))

But perhaps that’s as close as you can get in your Comal like language Ruby.


I made a file foo.rb:


def at_least n, list
list.any?{|x| eval x and 0 == n -= 1}
end

def foo a, b, c
puts at_least(2, %w(a b c puts(6) 1==1 puts(7) 2==2 puts(8)))
end

puts foo(10, 20, 30)


And call it:
$ ruby foo.rb
foo.rb:2:in `at_least': undefined local variable or method `a' for
main:Object (NameError)
from foo.rb:9:in `eval'
from foo.rb:2:in `at_least'
from foo.rb:9:in `any?'
from foo.rb:2:in `each'
from foo.rb:2:in `any?'
from foo.rb:2:in `at_least'
from foo.rb:6:in `foo'
from foo.rb:9

How can I make this work?
In principle it should just print out “true”.
Or if I call it like: puts foo(false, false, false) then it should
print 6 and 7 and true.
The 2==2 would end this routine before puts(8) gets evaled.

My version can do it:
(defn foo [a b c]
(at-least-two a b c (pr 6) (= 1 1) (pr 7) (= 2 2) (pr 8)))

When I call it (foo false false false) it prints 6 and 7 and returns
true.
The call (foo false true false) prints just the 6 before it returns true.

Leandro Rios

unread,
Jan 24, 2009, 11:24:56 AM1/24/09
to
Leandro Rios escribió:

Just learned that I'm wrong, so please disregard. *sigh*

Leandro.

André Thieme

unread,
Jan 24, 2009, 11:32:03 AM1/24/09
to
Leandro Rios schrieb:

> Isn't filter evaluating all of its arguments when at-least-two should
> stop when (precisely) at least two of them evaluated to true? And why is
> take necessary? Wouldn't it be the same to say

filter is lazy. It only visits as many objects out of the collection as
required.
count consumes its argument completely. So without the (take 2 ..) count
would in fact take the lazyness of filter away.


> (defmacro at-least-two [& x]
> `(> 1 (count (filter eval '~x)))) ?
>
> Don't forget: We are counting chars! :)

Hehe :-)


> Disclaimer:I didn't look too closely to Clojure, so it's very possible
> that I'm wrong.

Well, your version is not too far off.
But it has a few problems.
First of all, it can’t work with lexical variables:


(let [a nil, b false, c true]

(at-least-two a b c (= 5 6) 8 9))
==> Exception: Unable to resolve symbol: a in this context


Also you got the > wrong. It should be <.
Because otherwise:
(at-least-two true true) ==> false

But even if we correct that then count will consume everything that
filter could possibly return. If we do:
(at-least-two true true (print "Hi"))
count will force filter to also look at (print "Hi") which returns nil.
But that doesn’t matter, as this print should not be evaluated. Your
version prints “Hi”.

André Thieme

unread,
Jan 24, 2009, 11:36:01 AM1/24/09
to
Leandro Rios schrieb:

> Just learned that I'm wrong, so please disregard. *sigh*

It was a good try. You are not too far away from the real solution.
So far in this thread only three versions are able to do what is
required. The first one, from Rainer in Common Lisp.
Also the one from Kaz, also Common Lisp, where he implemented the
right tools reproduce my Clojure solution in CL.
And my version, in Clojure.

The one from Jillian James does not work. He was not capable of
providing a function that takes any number of args. But even if
we disregard that, his current version has the problem with local
variables.

The most unfortunate fact is, that the solution of the thread starter
is also wrong. He wanted to demonstrate how easy one can write macros
with NewLisp, but he left out the case with lexical variables so far.

Hasta luego :-)

Kaz Kylheku

unread,
Jan 24, 2009, 3:52:38 PM1/24/09
to
On 2009-01-24, André Thieme <address.good.un...@justmail.de> wrote:
> Kaz Kylheku schrieb:
>> On 2009-01-24, jos...@corporate-world.lisp.de <jos...@corporate-world.lisp.de>
>> wrote:
>>> On Jan 24, 2:13 am, André Thieme <address.good.until.
>>> 2009.may...@justmail.de> wrote:
>
>>>> (defmacro at-least-two [& x]
>>>> `(= 2 (count (take 2 (filter eval '~x)))))
>>> It takes the first two and counts those?
>>>
>>> Shouldn't it count the expressions that evaluate to true over all
>>> items?
>>
>> Once TAKE 2 pulls out two, it is done. Pardon me, it must be COUNT that
>> actually pulls out the items.
>
> Yes, you are absolutely right. Did you know that because you already
> worked in Clojure? Or was this the result of your analysis?

No. My secret is that no matter what programming language I'm working
with, I mentally it to Fortran. So I basically know every language.

:)

Just kidding.

The fact that the operations are lazy can be deduced from knowing the meaning
of what you are trying to do, and seeing what kind of meanings can be assigned
to the constituent pieces of syntax to bring about that result.

> > (TAKE 2) does nothing, just produces a virtual sequence.
>
> Exactly.

Right, to the extent, of course, that producing a virtual sequence is
considered ``nothing''. :)

> And thanks for your translation into CL.

That's a bad translation, by the way, because pulling items from the lazy
sequences destroys the lazy sequence.

It was just a quick way to get the same semantics from essentially the same
syntax.

Concrete example: for instance. We should be able to do this (list (lazy-count
s) (lazy-count s)) and obtain a list of two equal integers. The second call to
lazy-count should not find the sequence to be empty.

Kaz Kylheku

unread,
Jan 24, 2009, 3:55:56 PM1/24/09
to
On 2009-01-24, André Thieme <address.good.un...@justmail.de> wrote:
> foo.rb:2:in `at_least': undefined local variable or method `a' for
> main:Object (NameError)
> from foo.rb:9:in `eval'
> from foo.rb:2:in `at_least'
> from foo.rb:9:in `any?'
> from foo.rb:2:in `each'
> from foo.rb:2:in `any?'
> from foo.rb:2:in `at_least'
> from foo.rb:6:in `foo'
> from foo.rb:9
>
> How can I make this work?

This looks like the result of lexical scope.

Install someone's patch that adds ``declare special a;'' syntax. :)

André Thieme

unread,
Jan 24, 2009, 4:30:55 PM1/24/09
to
Kaz Kylheku schrieb:

The global namespace will thank us for such a patch.

Majorinc Kazimir

unread,
Jan 24, 2009, 5:51:49 PM1/24/09
to
jos...@corporate-world.lisp.de wrote:

> Still with the name capture problems.

;---------

First, I must say that it is not the problem in Newlisp
practice, because Newlisp users, like CL and Scheme users,
in practice use lexical scope mechanisms.


But, from purely theoretical point of view, even without
lexical scope, I'll show the idea how one can make dynamic
scope safe. This is how instance of your code looks like:

(LET ((#:G14745 0))
(OR (AND (= 1 1) (= (INCF #:G14745) 2))
(AND (= 3 2) (= (INCF #:G14745) 2))
(AND (= 2 2) (= (INCF #:G14745) 2))
(AND (= 4 4) (= (INCF #:G14745) 2))))

Each instance is different, due to that #:G14 ...
Macro is your meta code that acomplishes that.

I can do the same. I just need TO USE something
like this for each instance of my fexpr call.

(define-macro (at-least at-least.n.14745)
(let ((at-least.c.14745 0))
(doargs (at-least.i.14745
(= at-least.c.14745 at-least.n.14745))
(if (eval at-least.i.14745)
(inc at-least.c.14745)))
(>= at-least.c.14745 at-least.n.14745)))

And of course, that each instance is different.

It is possible. Not trivial, but routine job, which
can be factored out in the function and
called with (protect2 safe at-least '(x cnt)).

Details on my blog.

(Again, it is theoretical issue, Newlispers in
practice use lexical scope.)

;--------

In this particular case, it is not needed, however. Naming
convention is good enough. For example - this one:

(define-macro (at-least at-least/n)
(let ((at-least/c 0))
(doargs (at-least/i (= at-least/i at-least/n))
(if (eval at-least/i)
(inc at-least/c)))
(>= at-least/c at-least/n)))

With such naming convention,

* It is impossible that one accidentally overshadow variable
* It is also impossible that any expression that contains
FREE occurence of symbol at-least/ is passed to
(eval at-least/x) where it can make some harm.

Because naming convention is good enough, I think it is
fair to present code as it is, without naming convention
that might be important if fexpr is intended for more
general use. Transformation from "ordinary naming" to naming
convention is trivial and it can be automatized.

;---------------------

The flexibility issue:

Lets say I want expressions accepting variable number
of occurences of n. I need only minor changes to
my original program:

(define-macro (at-least atn)
(let ((aten (eval atn)))
(doargs(ati (zero? aten))
(when (eval ati)
(dec aten)))
(zero? aten)))

;test
(let ((x 1) (y 2) (z 3) (n 3))
(println (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))))))
(exit)
It works.

-> nil
change 1000 to 100 -> true

Only simple operations (let, doargs, when, zero?, eval).
Accepts variables, expressions and 0 as number of occurences.

jos...@corporate-world.lisp.de

unread,
Jan 24, 2009, 6:51:31 PM1/24/09
to
On 24 Jan., 23:51, Majorinc Kazimir <fa...@email.address> wrote:
> jos...@corporate-world.lisp.de wrote:
> > Still with the name capture problems.
>
> ;---------
>
> First, I must say that it is not the problem in Newlisp
> practice, because Newlisp users, like CL and Scheme users,
> in practice use lexical scope mechanisms.

The problem is independent of scope mechanisms.

>
> But, from purely theoretical point of view, even without
> lexical scope, I'll show the idea how one can make dynamic
> scope safe. This is how instance of your code looks like:
>
> (LET ((#:G14745 0))
>    (OR (AND (= 1 1) (= (INCF #:G14745) 2))
>        (AND (= 3 2) (= (INCF #:G14745) 2))
>        (AND (= 2 2) (= (INCF #:G14745) 2))
>        (AND (= 4 4) (= (INCF #:G14745) 2))))
>
> Each instance is different, due to that #:G14 ...
> Macro is your meta code that acomplishes that.

It is safe because the macro generates a symbol that
no other piece of code can access. It is a symbol
that is not interned, so it can't be retrieved
by name or other means. No other code running inside
my generated code can access it - only where
my macro has generated the necessary forms.

See 'hygienic macros' in Scheme.

...

> In this particular case, it is not needed, however. Naming
> convention is good enough. For example - this one:
>
> (define-macro (at-least at-least/n)
>           (let ((at-least/c 0))
>                (doargs (at-least/i (= at-least/i at-least/n))
>                        (if (eval at-least/i)
>                            (inc at-least/c)))
>                (>= at-least/c at-least/n)))
>
> With such naming convention,
>
> * It is impossible that one accidentally overshadow variable
> * It is also impossible that any expression that contains
>    FREE occurence of symbol at-least/ is passed to
>    (eval at-least/x) where it can make some harm.

You are saying for this example. I would need to inspect
EVERY piece of code to see if that is really true, since
EVAL may evaluate code in some scope and
may overwrite values from outer bindings.
There is no language mechanism that prevents
that, since the code you have posted here
is not lexically scoped. This means you have to
look at every code that potentially runs inside
some of your FEXPRs. BAD, BAD. With lexical
binding and no EVAL access to local variables
it can't happen - it is just not possible. This
makes it possible in Scheme and CL to pass
around code without scope worries - BY DEFAULT.

>
> Because naming convention is good enough, I think it is
> fair to present code as it is, without naming convention
> that might be important if fexpr is intended for more
> general use. Transformation from "ordinary naming" to naming
> convention is trivial and it can be automatized.


You have to make sure that nested at-least will work

(at-least 1 (atleast 1 (foo-p))

It may work in this case (I'm not curious enough to find out),
but in general it is something to be checked.

...

> (define-macro (at-least atn)
>        (let ((aten (eval atn)))
>             (doargs(ati (zero? aten))
>                    (when (eval ati)
>                          (dec aten)))
>             (zero? aten)))
>
> ;test
> (let ((x 1) (y 2) (z 3) (n 3))
>     (println (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))))))

If I bind EVAL around it, what happens? Will some instance of AT-LEAST
use
the new EVAL? If that is the case then every function name is
a potential problem.

> (exit)
> It works.
>
> -> nil
> change 1000 to 100 -> true
>
> Only simple operations (let, doargs, when, zero?, eval).

EVAL is a simple operation? Cough. I thought behind EVAL
is a whole interpreter with the mentioned features like
executing code in the calling scope.

> Accepts variables, expressions and 0 as number of occurences.

The name capture stuff you constantly have in your code
makes me nervous. The whole thing that the evaluated
code runs inside the scope of the library functions
is just wrong and an invitation to make errors. In the
case of my macro I have showed that the macro generates
the code and thus the code is not running 'inside'
the macro. In other cases the code has its own
lexical environment BY DEFAULT and not by something
I have to care for.

It may be that Newlisp is only used for relatively simple code
or that the developers follow some discipline
(though your code is not an example, since FEXPR
issues are visible in the examples you have posted here),
but in the history FEXPRs have been used in much more
complex code (like interpreters for other languages written
in Lisp) and it was found to be a bad idea - even worse
that overused macros.

William James

unread,
Jan 25, 2009, 8:42:30 AM1/25/09
to
Kaz Kylheku wrote:

> (defun at-least-n (n-func &rest funcs)
> (loop with limit = (funcall n-func)
> with c = 0
> for f in funcs
> when (funcall f) do (incf c)
> when (>= c limit) do (return t)))
>
> (defmacro lambda-call (function &rest exprs)
> `(funcall ,function ,@(mapcar (lambda (expr) `(lambda nil ,expr))
> exprs)))
>
> (lambda-call 'at-least-n 3 (print 'a) (print 'b) nil (print 'c)
> (print 'd))
>
> -> T
>
> output:
> A
> B
> C
>
> Though LAMBDA-CALL is a special form, AT-LEAST-N is a first class
> function that takes functional arguments:
>
> (at-least-n (lambda () 3) (lambda () (print 'a)) ...)
>

> There is no need to have functions that take unevaluated arguments,
> because you can make a special operator like LAMBDA-CALL that gives
> you syntactic sugar into turning forms into the bodies of simple
> functions, and passing the resulting functions to a function. The
> function then uses FUNCALL to ``evaluate'' these compiled bodies
> rather than EVAL. (Above, the function is FUNCALL).

Ruby:

def _ &blk; proc &blk; end
==>nil

def at_least n, list
list.any?{|x| x.call != false and 0 == n -= 1}
end
==>nil

at_least 3, [_{p :A}, _{false}, _{p :B}, _{p :C}, _{p :D}]
:A
:B
:C
==>true

Majorinc Kazimir

unread,
Jan 26, 2009, 5:43:45 PM1/26/09
to

> It may be that Newlisp is only used for relatively simple code
> or that the developers follow some discipline

Newlispers mostly use statical scope mechanisms if
they want safety. Thats why discussion of the fexpr
safety is mostly theoretical issue.

>> * It is impossible that one accidentally overshadow variable
>> * It is also impossible that any expression that contains
>> FREE occurence of symbol at-least/ is passed to
>> (eval at-least/x) where it can make some harm.
>
> You are saying for this example. I would need to inspect
> EVERY piece of code to see if that is really true, since
> EVAL may evaluate code in some scope and
> may overwrite values from outer bindings.

If two fexprs use variables with different
names - no collision can occur. No chance. Naming convention
is enough for all cases except for fexprs recursively
calling itself, passing expressions with free inner
variables. In that case, one need to apply the technique
described in my blogpost: not all fexprs use different variables,
but all instances of fexpr call use different variables.

Funarg problem cannot survive that.

>
>> (define-macro (at-least atn)
>> (let ((aten (eval atn)))
>> (doargs(ati (zero? aten))
>> (when (eval ati)
>> (dec aten)))
>> (zero? aten)))
>>
>> ;test
>> (let ((x 1) (y 2) (z 3) (n 3))
>> (println (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))))))
>
> If I bind EVAL around it, what happens? Will some instance of AT-LEAST
> use
> the new EVAL? If that is the case then every function name is
> a potential problem.

Redefining eval is pretty agressive idea! You are not
willing even to use it, less to redefine it.

Yes, all instances of at-least (and everything else) will
use new eval. It is expected behaviour in dynamic scope.

But if you want binding in "lexical scope" fashion, it is
possible - and easy. Instead of

(let ((ewal [replacement])) ; it is dynamic scope let
[some code]) ;

One has to write:

(let ((G174 [replacement])) ; G174 not in [some-code]
(eval (let ((ewal G174))
(expand '[some code] ; replace ewal with G174
'ewal))) ; in [some code] code but
; nowhere else.

Do you see what I did? I textually replaced eval with
G174, in the intended lexical scope and nowhere else, and
then evaluated it.

With few additional technical details, I can write fexpr with
syntax

(let-static ((ewal [replacement]))
[some code])

You gave me idea for my next blog post.

(I used ewal because eval is protected symbol,
it can be changed, but it requires few extra technical
details.)

Majorinc Kazimir

unread,
Jan 27, 2009, 9:26:04 AM1/27/09
to
Second call:

>>
>>> (define-macro (at-least atn)
>>> (let ((aten (eval atn)))
>>> (doargs(ati (zero? aten))
>>> (when (eval ati)
>>> (dec aten)))
>>> (zero? aten)))
>>>
>>> ;test
>>> (let ((x 1) (y 2) (z 3) (n 3))
>>> (println (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))))))

==> nil
==> true if you replace 1000 with 100

At least n is generalized or, (or ...) = (at-least 1 ...),
It has to be macro to be lazy - and to have consistent
syntax with OR. As you can see, this one works
as (at-least 0 ...) as well.

As far as it goes, only Rainier presented two solutions
for slightly simpler versions of the problem that really
worked. Two very short, complicated, but not completely
successeful attempts in Clojure, and one long with
functional syntax in Lisp + some Ruby.

Anyone else (except Rainier) to implement it in
his favorite CL, Clojure, Scheme ...?

Raffael Cavallaro

unread,
Jan 27, 2009, 2:36:57 PM1/27/09
to
On 2009-01-27 09:26:04 -0500, Majorinc Kazimir <fa...@email.address> said:

> Anyone else (except Rainier) to implement it in
> his favorite CL, Clojure, Scheme ...?

here's a recursive common lisp version:

(defmacro at-least (n &rest es)
`(cond ((null ',es) nil)
((< ,n 2) (or ,@es))
(t (at-least (if ,(car es) (1- ,n) ,n) ,@(cdr es)))))

--
Raffael Cavallaro, Ph.D.

joseph...@gmail.com

unread,
Jan 27, 2009, 3:26:02 PM1/27/09
to

Before you bother us with requests to implement this macro in other
Lisp dialects, why don't you take on the challenge of implementing
what Rainier did in CL, namely have *your* macro be safe from variable
capture, and result in code that can be compiled to machine code for
speed of execution without changing the semantics?

That is, why don't you show some sign of recognizing some of the basic
facts that the Lisp world discovered in the 1970s and 1980s, and quit
insisting your broken 1970s-era "new" technology is beneficial?

Rainer Joswig

unread,
Jan 27, 2009, 3:38:22 PM1/27/09
to
In article <glnnok$muc$1...@aioe.org>,
Raffael Cavallaro <raffaelc...@pas.espam.s.il.vous.plait.mac.com>
wrote:

> (defmacro at-least (n &rest es)
> `(cond ((null ',es) nil)
> ((< ,n 2) (or ,@es))
> (t (at-least (if ,(car es) (1- ,n) ,n) ,@(cdr es)))))

Almost, but see this:

CL-USER 48 > (let ((a t) (b nil)) (at-least 3 b a b b))
NIL

CL-USER 49 > (defun test () (let ((a t) (b nil)) (at-least 3 b a b b)))
TEST

CL-USER 50 > (compile 'test)

compiles, compiles longer, and longer, ...

--
http://lispm.dyndns.org/

Raffael Cavallaro

unread,
Jan 27, 2009, 5:59:45 PM1/27/09
to
On 2009-01-27 15:38:22 -0500, Rainer Joswig <jos...@lisp.de> said:

> Almost, but see this:

serves me right for posting before coffee ;^)

(defmacro at-least (n &rest es &aux (carsym (gensym)))
(if (null es) nil
`(let ((,carsym ,(car es)))
(if (< ,n 2) (or ,@es)
(at-least (if ,carsym (1- ,n) ,n) ,@(cdr es))))))

regards,

Ralph

--
Raffael Cavallaro, Ph.D.

Majorinc Kazimir

unread,
Jan 27, 2009, 7:53:18 PM1/27/09
to


Almost there:

[1]---------------

> (set 'z 0)
0
> (at-least 1 (progn (incf z) nil))
NIL
> z
2
>

[2]----------------

> (at-least 0 nil nil)
NIL

It is more logical that at-least 0 is true.

--------------------

When is that macro expanded,
all during compile time,
or something during runtime?

Macroexpansion time and space is quadratic.
Does it means alot in CL?

Majorinc Kazimir

unread,
Jan 27, 2009, 7:57:48 PM1/27/09
to

> Lisp dialects, why don't you take on the challenge of implementing
> what Rainier did in CL, namely have *your* macro be safe from variable
> capture, and result in code that can be compiled to machine code for
> speed of execution without changing the semantics?

It is easy to write code safe from
variable capture is this case.
Just follow the naming convention.

Replace all variables like atn with at-least/n.

Variable capture is still possible
if you define another at-least/n somewhere
else in the code. But in is not much different
than defining another at-least.


I explained already how lexical scope can be
achieved in language with dynamic scope.

Raffael Cavallaro

unread,
Jan 27, 2009, 8:52:16 PM1/27/09
to
On 2009-01-27 19:53:18 -0500, Majorinc Kazimir <fa...@email.address> said:

>
> > (set 'z 0)
> 0
> > (at-least 1 (progn (incf z) nil))
> NIL
> > z
> 2
> >

the value of z is irrelevant here. Since progn only returns the value
of its last form,

(at-least 1 (progn t t t t t nil))

*should* return nil, since there's only one form, the progn, and its
value is nil.

> (at-least 0 nil nil)
> NIL
>
> It is more logical that at-least 0 is true.

I was just using the same semantics as Rainer's version which you
seemed to consider equivalent to yours. Here's Rainer's version:

? (defmacro at-least (n &rest es &aux (c (gensym)))
`(let ((,c 0))
(or ,@(loop for e in es collect `(and ,e (= (incf ,c) ,n))))))
AT-LEAST
? (at-least 0 nil)
;Compiler warnings :
; In an anonymous lambda form: Unused lexical variable #:G1371
NIL
?

If you want (at-least 0 ...) to always return t, just add a cond clause
for n=0:

(defmacro at-least (n &rest es &aux (carsym (gensym)))
(if (null es) nil
`(let ((,carsym ,(car es)))

(cond ((zerop ,n) t)
((= ,n 1) (or ,@es))
(t (at-least (if ,carsym (1- ,n) ,n) ,@(cdr es)))))))

? (at-least 0 nil)
;Compiler warnings :
; In an anonymous lambda form: Unused lexical variable #:G1648
T
?


--
Raffael Cavallaro, Ph.D.

Raffael Cavallaro

unread,
Jan 27, 2009, 8:59:05 PM1/27/09
to
On 2009-01-27 19:57:48 -0500, Majorinc Kazimir <fa...@email.address> said:

> I explained already how lexical scope can be
> achieved in language with dynamic scope.

Even assuming you manage to do so the real issue is one of sensible
defaults. Scheme showed clearly that the sensible default is lexical
scope because it allows programmers to reason about code without having
to do whole-program analysis. This is why lexical scope was adopted as
the default in Common Lisp. newlisp gets this backwards.
--
Raffael Cavallaro, Ph.D.

jos...@corporate-world.lisp.de

unread,
Jan 27, 2009, 9:22:21 PM1/27/09
to
On 28 Jan., 02:52, Raffael Cavallaro

<raffaelcavall...@pas.espam.s.il.vous.plait.mac.com> wrote:
> On 2009-01-27 19:53:18 -0500, Majorinc Kazimir <fa...@email.address> said:
>
>
>
> >  > (set 'z 0)
> > 0
> >  > (at-least 1 (progn (incf z) nil))
> > NIL
> >  > z
> > 2
>
> the value of z is irrelevant here. Since progn only returns the value
> of its last form,

the problem would be that the forms are executed twice. They should
be executed only once.

...

Dimiter "malkia" Stanev

unread,
Jan 27, 2009, 9:32:18 PM1/27/09
to

And Common Lisp, deciding to keep dynamic vars, unlike Scheme (AFAIK),
but making them a *special*, not default choice is the right thing to do!

This gives you power way above any other programming language.

Majorinc Kazimir

unread,
Jan 27, 2009, 9:35:28 PM1/27/09
to
Raffael Cavallaro wrote:
> On 2009-01-27 19:53:18 -0500, Majorinc Kazimir <fa...@email.address> said:
>
>
>
>>
>> > (set 'z 0)
>> 0
>> > (at-least 1 (progn (incf z) nil))
>> NIL
>> > z
>> 2
>> >
>
> the value of z is irrelevant here. Since progn only returns the value of
> its last form,
>
> (at-least 1 (progn t t t t t nil))
>
> *should* return nil, since there's only one form, the progn, and its
> value is nil.


It is OK that it returns NIL, but it is
not irrelevant that he somehow returned
z to be 2. At-least 1 should work like OR.

Ok for that (at-least 0...).

Kaz Kylheku

unread,
Jan 27, 2009, 9:41:32 PM1/27/09
to
On 2009-01-28, Majorinc Kazimir <fa...@email.address> wrote:
> I explained already how lexical scope can be
> achieved in language with dynamic scope.

It's not lexical scope, because you can't return a lambda from it which
always refers to the correct variables.

Rob Warnock

unread,
Jan 27, 2009, 10:18:00 PM1/27/09
to
Dimiter \"malkia\" Stanev <mal...@mac.com> wrote:
+---------------

| Raffael Cavallaro wrote:
| > Even assuming you manage to do so the real issue is one of sensible
| > defaults. Scheme showed clearly that the sensible default is lexical
| > scope because it allows programmers to reason about code without having
| > to do whole-program analysis. This is why lexical scope was adopted as
| > the default in Common Lisp. newlisp gets this backwards.
|
| And Common Lisp, deciding to keep dynamic vars, unlike Scheme (AFAIK),
| but making them a *special*, not default choice is the right thing to do!
+---------------

Unfortunately, for compatibility with historical Lisps, Common Lisp
chose to leave global variables SPECIAL by default, *not* lexical
as in Scheme. This was a serious mistake, IMHO.

Fortunately, the addition of DEFINE-SYMBOL-MACRO to the standard
made it possible for users to re-introduce global lexical variables
themselves.[1]


-Rob

[1] <http://rpw3.org/hacks/lisp/deflex.lisp> and the like.

-----
Rob Warnock <rp...@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607

Didier Verna

unread,
Jan 28, 2009, 3:06:33 AM1/28/09
to
rp...@rpw3.org (Rob Warnock) wrote:

> Unfortunately, for compatibility with historical Lisps, Common Lisp
> chose to leave global variables SPECIAL by default, *not* lexical as
> in Scheme. This was a serious mistake, IMHO.

Why ? I rarely use global variables, but when I do, I usually find it
very convenient that they are dynamically scoped by default (sort of
like Emacs's user options). Do you have a frequent use-case where you'd
prefer them to be lexical ?

--
European Lisp Symposium, May 2009: http://www.european-lisp-symposium.org

Scientific site: http://www.lrde.epita.fr/~didier
Music (Jazz) site: http://www.didierverna.com

EPITA/LRDE, 14-16 rue Voltaire, 94276 Le Kremlin-Bicętre, France
Tel. +33 (0)1 44 08 01 85 Fax. +33 (0)1 53 14 59 22

Rob Warnock

unread,
Jan 28, 2009, 8:21:53 AM1/28/09
to
Didier Verna <did...@lrde.epita.fr> wrote:
+---------------

| rp...@rpw3.org (Rob Warnock) wrote:
| > Unfortunately, for compatibility with historical Lisps, Common Lisp
| > chose to leave global variables SPECIAL by default, *not* lexical as
| > in Scheme. This was a serious mistake, IMHO.
|
| Why? I rarely use global variables, but when I do, I usually find it

| very convenient that they are dynamically scoped by default (sort of
| like Emacs's user options). Do you have a frequent use-case where you'd
| prefer them to be lexical ?
+---------------

Noodling about in the REPL, mainly, where short (1-char, even) names
are *so* tempting and yet *so* likely to get you in trouble, usually by
accidentally converting some innocent lexical binding deep in your program
into a dynamic binding... whereupon hilarity ensues. [Or true disaster.]
Having global lexicals as the default (e.g., Scheme) or at least having
a convenient mechanism for global lexicals (CL + DEFLEX macro) completely
side-steps the problem:

cmu> (deflex x 3)

X
cmu> (deflex y 4)

Y
cmu> (defun xy () (list x y))

XY
cmu> (let ((x 1)
(y 2))
(list* x y (xy)))

(1 2 3 4)
>

Truth be told, I don't often use global lexicals in final production
code... *except* in the case where some kind of command line is
being provided to the user in which variable names might be typed as
arguments to commands. Parenthephobic users also tend to be earmuff shy.
A real-life example is my "hwtool" pattern [about which I've posted
here before], which is a CL plus OPFR[1] plus some PEEK/POKE/MMAP
utilities plus an application-specific library of user-oriented
functions -- "commands" to be typed to the OPFR REPL. The commands
aren't the problem; it's various global constants, such as names or
devices, registers, or bits. Stuff like this [example only, not real]:

hwtool> dump32 dev1 dev1-size
0x48a92bc8: 0x0000003a 0x00000010 0x00000070 0x00000094
0x48a92bd8: 0x00000030 0x0000000c
hwtool> disable dev1 dev1-control dev1-intr-en
:OK
hwtool> dump32 dev1 dev1-size
0x48a92bc8: 0x0000003a 0x00000000 0x00000070 0x00000094
0x48a92bd8: 0x00000030 0x0000000c
hwtool>

Sure, the user could be told to type "dump32 *dev1* +dev1-size+",
but the results of trying to force that sort of silliness on them
would be... uh... not comfortable for the one doing the forcing.


-Rob

joseph...@gmail.com

unread,
Jan 28, 2009, 9:24:04 AM1/28/09
to
On Jan 27, 7:57 pm, Majorinc Kazimir <fa...@email.address> wrote:
> > Lisp dialects, why don't you take on the challenge of implementing
> > what Rainier did in CL, namely have *your* macro be safe from variable
> > capture, and result in code that can be compiled to machine code for
> > speed of execution without changing the semantics?
>
> It is easy to write code safe from
> variable capture is this case.
> Just follow the naming convention.
>
> Replace all variables like atn with at-least/n.
>
> Variable capture is still possible
> if you define another at-least/n somewhere
> else in the code. But in is not much different
> than defining another at-least.

Meaning, you haven't actually achieved safety. Perhaps your choice of
language has something to do with this.

This is not just a theoretical problem: the lack of this kind of
safety makes it unsafe to combine libraries, or even to use your
macros at different levels of abstraction. That is, if your low-level
code uses magic names like "at-least/n", your higher-level code
calling the low-level code cannot also use your at-least macro for
high-level purposes without running the risk of the name conflict
causing impossible-to-debug problems.

This is analogous to the use of global storage making a subroutine non-
reentrant.

Anyone who suggested a new programming language that did not allow
functions with arguments or local variables, in favor of global
variables following a naming convention would be laughed at.

Majorinc Kazimir

unread,
Jan 28, 2009, 12:57:11 PM1/28/09
to
josepho...@gmail.com wrote:


>
> This is not just a theoretical problem: the lack of this kind of
> safety makes it unsafe to combine libraries, or even to use your
> macros at different levels of abstraction.


Newlisp HAS static scope support.

Newlisp users typically use that static scope support.

I just do not discuss it now.

I discuss dynamic scope because I claim it is powerful
and safe.

>
> This is analogous to the use of global storage making a subroutine non-
> reentrant.
>
> Anyone who suggested a new programming language that did not allow
> functions with arguments or local variables, in favor of global
> variables following a naming convention would be laughed at.

Newlisp supports local variables with dynamic scope.

It solves 75% of problems you speak about.

Other 25% is solved by variable names.

Just like in CL. Do you remember gensym?

I do the same in Newlisp. I can do it
manually, on the way I described, or
automatically on the way described in
my blog. Just like you do using gensym in
your macros.

You do not know that Newlisp support lexical scope,
and that Newlisp support local variables, and you
didn't read detailed explanation how to
protect dynamic scope, and you still want
to argue I'm wrong.


Raffael Cavallaro

unread,
Jan 28, 2009, 1:29:49 PM1/28/09
to
On 2009-01-27 21:22:21 -0500, "jos...@corporate-world.lisp.de"
<jos...@corporate-world.lisp.de> said:

> the problem would be that the forms are executed twice. They should
> be executed only once.
>
> ...

right, missed the multiple evaluation:

(defmacro at-least (n &rest es &aux (nsym (gensym)) (carsym (gensym))
(cdrsym (gensym)))
(if (null es) nil
`(let* ((,nsym ,n) (,carsym ,(car es)) (,cdrsym ',(cdr es)))
(cond ((zerop ,nsym) t)
((= ,nsym 1) (or ,carsym ,@`,(cdr es)))
(t (at-least (if ,carsym (1- ,nsym) ,nsym) ,@`,(cdr es)))))))

Robert Maas

unread,
Jan 28, 2009, 1:31:07 PM1/28/09
to
> From: Majorinc Kazimir <fa...@email.address>
> I define macro (at-least-two arg1 ... argn)
> that returns true if and only if at least
> two of args evaluate to the true. Macro is
> needed because we want lazy evaluation
> of arg1 to argn, like in OR or AND.

I presume order of evaluation is required to be arg1 then arg1 etc.
and finally argn only if needed to determine the result. Note that
the very last arg needs to be evaluated *only* if the count up to
that point was exactly 1. If the count was 0, then adding 1 can't
yield 2, so we can return FALSE already. If the count was 2, we
already returned TRUE even by obvious algorithm. Thus the algorithm
must maintain two counters, one telling the number of true results
needed to return true, and one telling the number of false results
needed to return false, and whenever either counter is exhausted
the procedure can immediately return that value.

The algorithm is then to initialize the two counters, then evaluate
one form at a time, bumping the appropriate counter per the result
of evaluation, and immediately returning if the bumped counter
reaches zero. Since we *always* bump a counter, one or the other,
if we initialized correctly then exactly one counter will be
exhausted *before* running off the end of the list of forms, thus
we never need a special case return value when running off the end.
(But we *do* need a special case during initialization to
immediately return if the arglist was empty.)

The code to be generated therefore is something like:
(initialize-counters)
(if arg1 (bump-true) (bump-false)) ;Actual form arg1 here!
(if arg2 (bump-true) (bump-false)) ;Actual form arg2 here!
(if arg3 (bump-true) (bump-false)) ;Actual form arg3 here!
...
(if arg1 (bump-true) (bump-false)) ;Actual form argn here!
(error "Bug: fell off end")
where
(bump-true) is defined as
(if (zerop (decf true-counter)) (return true))
and
(bump-false) is defined as
(if (zerop (decf false-counter)) (return true))
Presumably all that code would be inside a BLOCK form, although the
return value of that (result from last form) is never used, but the
tag for that block *is* used by the return call within each
bump-true or bump-false form.

Now we get into the details:

The two containers ought to be mutable slots in some container.
Whether that container is a LET frame (slots are lexical variables
in it) or a CONS cell (slots are CAR and CDR) or a LIST (slots are
CAR and CADR) or a defstruct etc. is an implemtatoin choice, but
LET slots have a disadvantage, that if bump-true and bump-false are
first-class functions whose definitions lexically scoped outside
this block then they have no access to the LET slots as lexical
variables, thereby precluding optimization for minimal storage by
calls to a single copy of each bump function. Doing parameter-pass
of the pointer to a first-class container and the doing a slot
reference within that container takes more CPU cycles than doing a
direct lexical reference to LET slots, so if you know you won't
ever be optimizing for space then LET is slightly faster. But the
amount of speed gain is so small, I hate to sacrifice the
lexibility of being able to optimize for speed or space without
changing the code. So I advise using a first-class container
instead.

To make the code space-efficient when running interpreted, and to
allow the compiler to recognize shared code and thus automatically
make a first-class anonymous function to call, the repeated
sub-expressions for (bump-true) and (bump-false) should be
respectively EQ.

To avoid the chance of the FRAME tag matching a symbol used
elsewhere, the FRAME tag should be a non-interned symbol which is
generated anew for each expansion of an at-least-two macro form.
But the FRAME tag is an *ordinary* (not keyword) symbol, so it has
a VALUE slot, so we can save some code by using the VALUE cell of
that symbol as the pointer to the counters object.

Generalizing to (at-least num form1 form2 ... formn):
All that does is change the algorithm for initializing the two
counters. With the special case at-least-two, true-counter is
always initialized 2 and false-counter is initialized n-1. With the
general case, true-counter is initialized num, and false-counter is
initialized n+1-num. The special code to detect trivial cases is
simply to immediately return if either of these counters is already
non-positive after initialization. Thus:
(when (not (plusp (setf true-counter num) (return true))
(when (not (plusp (setf false-counter (+ n 1 (- num))))) (return false))
Note that calling ZEROP here is not sufficient, because with
extremely redundant code you can overshoot the zero point. For
example, (at-least 4 form1 form2) initializes true-counter to 4
(positive integer, no problem) but false-counter to -1 (already
negative, ZEROP would fail). But later when you know the counters
are both positive integers to start, ZEROP works because
decrementing an integer by exactly 1 can never jump across zero.

Putting together all those details, including using CAR (=
true-counter) and CDR (= false-counter) of the CONS cell which is
linked from the VALUE slot of the BLOCK tag, the result of macro
expanansion of (at-least num form1 form2 ... formn) ought to be
something like:
(let ((mynum num)) ;The form num is evaluated just once here
(block #tag#
(setq #tag# (ncons nil)) ;(make-list 1) is more "correct" but slower
(when (not (plusp (setf (car #tag#) mynum)
(return-from #tag# true))
(when (not (plusp (setf (cdr #tag#) (+ #n# 1 (- mynum)))))
(return-from #tag# false))
(if form1 #1=(if (zerop (decf (car #tag#))) (return-from #tag# true))
#2=(if (zerop (decf (cdr #tag#))) (return-from #tag# true)))
(if form2 #1# #2#)
(if form3 #1# #2#)
...
(if formn #1# #2#)
(error "Bug: fell off end")
))
where #tag# is generated by GENSYM or MAKE-SYMBOL each time an
at-least form is expanded, and #n# is computed by calling LENGTH of
the rest of the parameter list after the num parameter.

*WARNING* The above code might not work correctly if called
recursively, that if one of the forms inside an (at-least ...) form
makes a call to the *very*same*EQ* (at-least ...) form, such that
the inner and outer #tag#s are EQ, because inner invocation
clobbers the value cell of that symbol. This can be fixed by
introducing a second LET slot, which can be just an ordinary
literal symbol rather than an uninterned symbol. Thus the first
three lines of code would be replaced by:
(let ((mynum num) ;The form num is evaluated just once here
(counters (ncons nil)))
(block #tag#
and all references to (car #tag#) or (cdr #tag#) would be changed
to (car counters) or (cdr counters) respectively.
Of course all forms (return-from #tag# ...) would remain as-is.
If the compiler, optimizing for space, created anonymous functions
to hold the codes for
(if (zerop (decf (car counters))) (return-from #tag# true))
(if (zerop (decf (cdr counters))) (return-from #tag# true)))
it would need to pass the COUNTERS parameter explicitly to each,
unless it's smart enough to capture the lexical variable reference
somehow. Hmm, it might have the same problem with the original code
too. Of course hardly any compiler would be smart enough to notice
EQ sub-expressions and optimize for space by making a secret
function to be called, so this quibble may be moot. It might be
better to just have true-counter and false-counter be macros
themselves, which expand into inline code or function calls at the
discretion of whoever decides which version of the macros to use
for any given compilation. Perhaps there should be just one such
macro unifying the two counters in the container, something like:
(defmacro counter (counters tf)
(if `,tf `(car ,counters) `(cdr ,counters)))

Without my writing and testing the code, *especially* those last
two lines, there's no way for me to be sure I haven't made any
mistakes, so please treat the above as ideas for implementation of
the macro rather than something guaranteed to work.

P.S. I'd like some feedback from Pascal or Kent or Kaz as to
whether you consider my problem-analysis here to be reasonably
professional-quality. (Never mind that I don't feel like actually
spending the extra unpaid time to produce a working implementation
of my ideas here. If somebody wants me to do *that* work, somebody
pays me for it, OK?)

Raffael Cavallaro

unread,
Jan 28, 2009, 1:35:55 PM1/28/09
to
On 2009-01-28 13:29:49 -0500, Raffael Cavallaro
<raffaelc...@pas.espam.s.il.vous.plait.mac.com> said:

> (defmacro at-least (n &rest es &aux (nsym (gensym)) (carsym (gensym))


extra unused gensym in that one:

(defmacro at-least (n &rest es &aux (nsym (gensym)) (carsym (gensym)))
(if (null es) nil
`(let* ((,nsym ,n) (,carsym ,(car es)))


(cond ((zerop ,nsym) t)
((= ,nsym 1) (or ,carsym ,@`,(cdr es)))
(t (at-least (if ,carsym (1- ,nsym) ,nsym) ,@`,(cdr es)))))))

regards

Ralph

--
Raffael Cavallaro, Ph.D.

Raffael Cavallaro

unread,
Jan 28, 2009, 1:41:02 PM1/28/09
to
On 2009-01-28 12:57:11 -0500, Majorinc Kazimir <fa...@email.address> said:

> I discuss dynamic scope because I claim it is powerful
> and safe.

I think most of us here would disagree on this point. We think that
lexical scope is safer because it allows one to understand individual
pieces of code without knowing the entire program in detail. This is
why lexical scope is the default in scheme, and why in the
standardization of common lisp this feature of scheme was adopted.
--
Raffael Cavallaro, Ph.D.

Dimiter "malkia" Stanev

unread,
Jan 28, 2009, 2:22:59 PM1/28/09
to
Didier Verna wrote:
> rp...@rpw3.org (Rob Warnock) wrote:
>
>> Unfortunately, for compatibility with historical Lisps, Common Lisp
>> chose to leave global variables SPECIAL by default, *not* lexical as
>> in Scheme. This was a serious mistake, IMHO.
>
> Why ? I rarely use global variables, but when I do, I usually find it
> very convenient that they are dynamically scoped by default (sort of
> like Emacs's user options). Do you have a frequent use-case where you'd
> prefer them to be lexical ?
>

I feel the same way! I mean this was a selling feature to me, coming
from the C++ world.

For example in C++ I would have to save a couple of global variable's
states somewhere, do operation, then restore it. Either that, or make
all my functions take lots of variable arguments, or even worse - a
structure/object informing them of the context.

jos...@corporate-world.lisp.de

unread,
Jan 28, 2009, 2:43:15 PM1/28/09
to
On 28 Jan., 20:22, "Dimiter \"malkia\" Stanev" <mal...@mac.com> wrote:
> Didier Verna wrote:

The problem is that a global special variable makes ALL variables
of the same name to be special and there is no way to
tell the CL system that something is not special.

(defvar x 10)

Above declares X to be a global special variable.

(defun foo (x) ; <- X is special!
...
x ;<- this is now looking up the dynamic value
...)

Now the X in the parameter list is also special. Calling (foo 10)
dynamically binds X to 10 (!).

(let ((x ...) ; <- X is special and gets dynamically bound
...
x ; <- this is now looking up the dynamic value
...)

Now X is bound dynamically.

This can introduce subtle errors into programs.


Special global and local variables are useful, but once you
define a variable with DEFVAR or DEFPARAMETER, variables
with the same name in LET expressions and function
argument lists are also special and will get bound
dynamically. This is not always wanted and probably
not by default.


Kaz Kylheku

unread,
Jan 28, 2009, 3:51:16 PM1/28/09
to

Maybe you're not making effective use of C++?

Dynamically-scoped variables are a very simple concept that you could implement
with a few templates.

include <iostream>

dynamic_var<int> x;

void f()
{
std::cout << "x == " << x << std::endl;
}

g()
{
dynamic_rebind<int> x(x, 42);
f(); // f should print 42
}

The templates dynamic_var and dynamic_rebind are fairly easy to implement.
It's nowhere near half of Common Lisp, and can easily be well-specified
and reliably implemented. I.e. hardly a Greenspun job.

Saving and restoring variables is hardly a huge abstraction that goes over the
capabilities of C++. Programers have been saving and restoring globals since
the dawn of Von Neumann machines with registers.

Pascal J. Bourguignon

unread,
Jan 28, 2009, 4:25:01 PM1/28/09
to

Well you can implement dynamic variables over lexical variables. For example:


;; WARNING: C++ Code follows.
------(dynamic.c++)----------------------------------------------------------
#include <map>
#include <string>
#include <sstream>
#include <iostream>
#include <exception>

//#define TRACE_DYNSCOPE
#undef TRACE_DYNSCOPE

class AbstractDynamicVariable {
protected:
std::string name;
AbstractDynamicVariable* oldVariable;
public:
AbstractDynamicVariable(std::string aName):name(aName),oldVariable(0){}
virtual ~AbstractDynamicVariable(){}
inline const std::string& getName(){ return(name); }
inline AbstractDynamicVariable* getOldVariable(){return(oldVariable);};
inline void setOldVariable(AbstractDynamicVariable* aVariable){ oldVariable=aVariable; };
#ifdef TRACE_DYNSCOPE
virtual std::string id(){
std::ostringstream s;
s<<"(AbstractDynamicVariable "<<this<<" "<<name<<")";
return(s.str());
}
#endif
};


class DynamicScopeException:public std::exception {
protected:
std::string message;
public:
DynamicScopeException(const std::string& aMessage):message(aMessage){}
virtual ~DynamicScopeException() throw() {}
virtual const char* what() const throw() { return(message.c_str()); }
};

template <class Type>
class DynamicVariable : public AbstractDynamicVariable {
protected:
Type value;
public:
DynamicVariable(std::string aName,const Type& aValue);
DynamicVariable(const DynamicVariable<Type>& other);
~DynamicVariable();

inline Type& getValue(){ return(value); }
inline Type& setValue(const Type& aValue){ return(value=aValue); }

inline Type& operator=(const Type& aValue){ return(this->setValue(aValue)); }
template <class OtherType>
inline Type& operator=(const DynamicVariable<OtherType>& otherVariable){
return(value=otherVariable.getValue()); }
inline operator Type(){ return(value); }

#ifdef TRACE_DYNSCOPE
virtual std::string id(){
std::ostringstream s;
s<<"(DynamicVariable<"<<(typeid(value).name())<<"> "<<this<<" "<<name<<")";
return(s.str());
}
#endif
};


namespace DynamicScope {
typedef std::map<std::string,AbstractDynamicVariable*> VariableMap;
static VariableMap variables;

static void push(AbstractDynamicVariable* variable){
VariableMap::iterator it=variables.find(variable->getName());
if(it!=variables.end()){
variable->setOldVariable(it->second);
it->second=variable;
#ifdef TRACE_DYNSCOPE
std::cout<<"DynamicScope::push "<<variable->id()
<<" old was "<<(variable->getOldVariable()==0?"NIL":variable->getOldVariable()->id())
<<std::endl;
#endif
}else{
variables.insert(VariableMap::value_type(variable->getName(),variable));
#ifdef TRACE_DYNSCOPE
std::cout<<"DynamicScope::push "<<variable->id()
<<" old was NIL"<<std::endl;
#endif
}
}

static void pop(AbstractDynamicVariable* variable){
VariableMap::iterator it=variables.find(variable->getName());
if(it!=variables.end()){
if(variable->getOldVariable()==0){
#ifdef TRACE_DYNSCOPE
std::cout<<"DynamicScope::pop "<<variable->id()
<<" old was "<<"NIL"
<<std::endl;
#endif
variables.erase(it);
}else{
it->second=variable->getOldVariable();
#ifdef TRACE_DYNSCOPE
std::cout<<"DynamicScope::pop "<<variable->id()
<<" old was "<<(it->second==0?"NIL":it->second->id())
<<std::endl;
#endif
}
}
}

template <class Type>
static DynamicVariable<Type>* variable(const std::string& name){
#ifdef TRACE_DYNSCOPE
{
VariableMap::iterator it=variables.begin();
VariableMap::iterator end=variables.end();
std::cout<<"Visible Dynamic Variables:";
for(;it!=end;it++){
std::cout<<" "<<it->second->id();
}
std::cout<<std::endl;
}
#endif
VariableMap::iterator it=variables.find(name);
if(it!=variables.end()){
AbstractDynamicVariable* avar=it->second;
DynamicVariable<Type>* result=dynamic_cast<DynamicVariable<Type>*>(avar);
if(result){
return(result);
}else{
throw DynamicScopeException("The dynamic variable "+name
+" is not of type "+(typeid(*result).name()));
}
}else{
throw DynamicScopeException("Unbound dynamic variable "+name);
}
}
};


template <class Type>
DynamicVariable<Type>::DynamicVariable(std::string aName,const Type& aValue)
:AbstractDynamicVariable(aName),value(aValue)
{
DynamicScope::push(this);
}

template <class Type>
DynamicVariable<Type>::DynamicVariable(const DynamicVariable<Type>& other)
:AbstractDynamicVariable(other.getName()),value(other.getValue())
{
DynamicScope::push(this);
}

template <class Type>
DynamicVariable<Type>::~DynamicVariable()
{
DynamicScope::pop(this);
}


//------------------------------------------------------------------------


void f(){
std::cout<<"*X* = "<<DynamicScope::variable<int>("*X*")->getValue()<<std::endl;
std::cout<<"*Y* = "<<DynamicScope::variable<int>("*Y*")->getValue()<<std::endl;
std::cout<<std::endl;
}

void g(){
DynamicVariable<int> x("*X*",42);
DynamicVariable<int> y("*Y*",24);
f();
x=-42;
y=-y;
f();
}

void h(){
DynamicVariable<int> x("*X*",4);
f();
}

int main(){
try{
DynamicVariable<int> x("*X*",0);
DynamicVariable<int> y("*Y*",0);
f();
g();
h();
}catch(DynamicScopeException& e){
std::cout<<"Got exception: "<<e.what()<<std::endl;
}
return(0);
}

/*
-*- mode: compilation; default-directory: "~/src/" -*-
Compilation started at Wed Jan 28 22:20:54

SRC="/Users/pjb/src/dynamic.c++" ; EXE="dynamic" ; g++ -I. -L. -g3 -ggdb3 -o ${EXE} ${SRC} && ./${EXE} && echo status = $?
*X* = 0
*Y* = 0

*X* = 42
*Y* = 24

*X* = -42
*Y* = -24

*X* = 4
*Y* = 0

status = 0

Compilation finished at Wed Jan 28 22:20:55
*/


--
__Pascal Bourguignon__

Kaz Kylheku

unread,
Jan 28, 2009, 5:06:01 PM1/28/09
to

Sorry, Pascal, this looks like a silly implementation, because you're dealing
with all these strings. What for?

You can represent dynamic variables as normal C++ global variables with normal
external names. Their local rebindings can also have normal lexically scoped
names. What happens is that the global variable is remapped so that its binding
lives in the local variable.

Also, you may need this to be thread-safe.

Here is a sketch. First we define a basic API for saving and restoring a thread
local value, which just collapses into a simple pointer when we aren't
compiling for thread support.

#ifef CONFIG_POSIX_THREADS
#include <pthread.h>
#endif

class dynamic_variable_base {
private:
#ifdef CONFIG_POSIX_THREADS
pthread_key_t key;
#endif
void *location;
public:
dynamic_variable_base();
~dynamic_variable_base(); // non-virtual; not intended for polymoprhism
void *get_location();
void set_location(void *);
void set_global_location(void *l) { location = l; }
};

dynamic_variable_base::dynamic_variable_base()
{
#ifdef CONFIG_POSIX_THREADS
pthread_key_create(&key, 0);
#else
location = 0;
#endif
}

dynamic_variable_base::~dynamic_variable_base()
{
#ifdef CONFIG_POSIX_THREADS
pthread_key_delete(key);
#endif
}

void *dymnamic_variable_base::get_location()
{
void *l;
#ifdef CONFIG_POSIX_THREADS
if ((l = pthread_getspecific(&key)) != 0)
return l;
#else
return location; // fall back on global
#endif
}

void dynamic_variable_base::set_location(void *l);
{
#ifdef CONFIG_POSIX_THREADS
pthread_setspecific(&key, l);
#else
location = l;
#endif
}


Now we can make a template which is a bit like defvar:


template <typename T>
class dynamic_def : public dynamic_variable_base {
private:
T global;

public:
define_dynamic() // define with no value
{
set_global_location(&global); // inform
}

define_dynamic(const T &value)
: global(value)
{
set_global_location(&global);
}

// [ ... ] snip operators to make object behave
// like a T: assignment form T, conversion to T, etc.
// All accesses to the variable
// must go through get_location and set_location!

// example: assignment
const define_dynamic &operator = (const T &rhs)
{
T *where = static_cast<T *>(get_location());
*where = rhs;
return *this;
}

// There is one global location for all threads;
// so an external mutex should be used to share that.
};

template <typenname T>
class dynamic_rebind {
private:
void *saved_location;
T local;
public:
dynamic_rebind(dynamic_def &var, const T &val)
: saved_location(var.get_location())
: local(val)
{
var.set_location(&local);
}

~dynamic_rebind()
{
var.set_location(saved_location);
}

// [ ... ] snip operators to make object behave
// like a T: assignment form T, conversion to T, etc.

// Accesses via this dynamic_rebind instance go directly to T.
// Accesses via the global dynamic_def go through the lookup.
};

You can also provide a dynamic_ref class similar to dynamic_rebind,
except that it doesn't rebind, but only provides faster access to the
variable by caching its location.

Look ma, no strings attached.

Dimiter "malkia" Stanev

unread,
Jan 28, 2009, 5:16:51 PM1/28/09
to
Thanks Kaz, Pascal!

Although I think Kaz's solution won't work:

dynamic_rebind<int> x(x, 42);

here, the first 'x' and the second 'x' are one and same thing. Certain
compilers in this case would give you "uninitialized local variable 'x'
used".

but it could be tweaked, something like this:

x.rebind(42);

but I can't be sure.

Pascal solutions is all fine by me, and gives you more debugging info by
keeping the actual variable name, but a C++ freak would go away from
that implementation (not me again, by any means).

In fact inhouse we do have a scripting language, with close capabilities
to the stuff above, even the global vars are saved and loaded (depending
on the flags that were used for creation).

Adding binding to them now seems easier, than before :)

Thanks again guys, getting good C++ help here is just amazing!

Kaz Kylheku

unread,
Jan 28, 2009, 6:03:28 PM1/28/09
to
On 2009-01-28, Dimiter "malkia" Stanev <mal...@mac.com> wrote:
> Thanks Kaz, Pascal!
>
> Although I think Kaz's solution won't work:
>
> dynamic_rebind<int> x(x, 42);
>
> here, the first 'x' and the second 'x' are one and same thing.

Ah yes, the constructor arguments are already in the scope of the declared
name.

But of course you can use a different name.

You can then use either name to refer to the variable; the local one
will be more efficient.

dynamic_def<int> g_x;

{
dynamic_rebind<int> x(g_x, 42);

// use x for speed, since the variable is really located inside
// x now, and access to it can be inlined! I.e. x should have
// next to no overhead compared to just using int.
// Going through g_x will work, but it will do the location lookup.
}

> but it could be tweaked, something like this:
>
> x.rebind(42);

No good; we are relying on constructors and destructors to manage the binding.

Now you need an explicit x.unbind(), which you could forget.

Dimiter "malkia" Stanev

unread,
Jan 28, 2009, 6:15:14 PM1/28/09
to
>> x.rebind(42);
>
> No good; we are relying on constructors and destructors to manage the binding.
>
> Now you need an explicit x.unbind(), which you could forget.
>

Yup, good point! Thanks Kaz!

Pascal J. Bourguignon

unread,
Jan 28, 2009, 6:25:22 PM1/28/09
to
Kaz Kylheku <kkyl...@gmail.com> writes:

> On 2009-01-28, Pascal J. Bourguignon <p...@informatimago.com> wrote:
>> "Dimiter \"malkia\" Stanev" <mal...@mac.com> writes:
>>
>>> Didier Verna wrote:
>>>> rp...@rpw3.org (Rob Warnock) wrote:
>>>>
>>>>> Unfortunately, for compatibility with historical Lisps, Common Lisp
>>>>> chose to leave global variables SPECIAL by default, *not* lexical as
>>>>> in Scheme. This was a serious mistake, IMHO.
>>>> Why ? I rarely use global variables, but when I do, I usually find
>>>> it
>>>> very convenient that they are dynamically scoped by default (sort of
>>>> like Emacs's user options). Do you have a frequent use-case where you'd
>>>> prefer them to be lexical ?
>>>>
>>>
>>> I feel the same way! I mean this was a selling feature to me, coming
>>> from the C++ world.
>>>
>>> For example in C++ I would have to save a couple of global variable's
>>> states somewhere, do operation, then restore it. Either that, or make
>>> all my functions take lots of variable arguments, or even worse - a
>>> structure/object informing them of the context.
>>
>> Well you can implement dynamic variables over lexical variables. For example:

>> [...]


>> class AbstractDynamicVariable {
>> protected:
>> std::string name;
>> AbstractDynamicVariable* oldVariable;
>
> Sorry, Pascal, this looks like a silly implementation, because you're dealing
> with all these strings. What for?

Yes, I couldn't help doing some greenspunning, sorry.
[ DynamicScope::variable("XYZ")->getValue() === (symbol-value (find-symbol "XYZ")) ]


> You can represent dynamic variables as normal C++ global variables with normal
> external names. Their local rebindings can also have normal lexically scoped
> names. What happens is that the global variable is remapped so that its binding
> lives in the local variable.
>
> Also, you may need this to be thread-safe.
>
> Here is a sketch. First we define a basic API for saving and restoring a thread
> local value, which just collapses into a simple pointer when we aren't
> compiling for thread support.
>

> [... a much better implementation ...]


>
> Look ma, no strings attached.

Great!

--
__Pascal Bourguignon__

Majorinc Kazimir

unread,
Jan 29, 2009, 8:02:15 PM1/29/09
to

Still one bug:

> z
16
> (at-least 0 (progn (incf z) 1) (progn (incf z) 2))
T
> z
17

Look, I've macroexpanded your macro and seen what are
you doing. You are good programmer, no doubt. But man,

> (defmacro at-least (n &rest es &aux (nsym (gensym)) (carsym (gensym)))
> (if (null es) nil
> `(let* ((,nsym ,n) (,carsym ,(car es)))
> (cond ((zerop ,nsym) t)
> ((= ,nsym 1) (or ,carsym ,@`,(cdr es)))
> (t (at-least (if ,carsym (1- ,nsym) ,nsym) ,@`,(cdr es)))))))

you have 57 tokens of dense code already, without
redundancy. Two gensyms, ,@`, stuff - for such a simple
and natural idea as a generalization of "or."

Compare it with Newlisp -

(define-macro (at-least atn)
(let ((aten (eval atn)))
(doargs(ati (zero? aten))
(and (eval ati)
(dec aten)))
(zero? aten)))

just one loop, 18 tokens, everyone can understand it.
And Newlisp at-least is the first class macro, which can
be passed as argument or returned as result, applied,
mapped, analyzed and mutated during runtime. So, I think
I can drive my point home.

Still, it must be that CL macros have some advantage.
Yes they do - your CL macro can be efficiently compiled,
while my Newlisp macro (fexpr) cannot. Some fexprs can
be efficiently compiled, but not this one.

And that's it, for what its worth.

Kaz Kylheku

unread,
Jan 29, 2009, 8:49:27 PM1/29/09
to
On 2009-01-30, Majorinc Kazimir <fa...@email.address> wrote:
> you have 57 tokens of dense code already, without
> redundancy. Two gensyms, ,@`, stuff - for such a simple
> and natural idea as a generalization of "or."
>
> Compare it with Newlisp -
>
> (define-macro (at-least atn)
> (let ((aten (eval atn)))
> (doargs(ati (zero? aten))
> (and (eval ati)
> (dec aten)))
> (zero? aten)))
>
> just one loop, 18 tokens, everyone can understand it.

You have a point. Interpreting (what an frexp does) /is/ generally easier than
compiling (which is what a macro does). The frexp is simple in large part
because it doesn't have to deal with the problem ``how do I translate this form
into code that solves the problem of implementing the form's behavior'', but
only with the problem ``how do I implement this form's behavior''.

Worthwhile things don't always come at zero cost. You rarely get something for
nothing.

> So, I think I can drive my point home.

You can, but in order to avoid being a hypocrite about technology,
you should be pulling that point home with a horse and cart.

André Thieme

unread,
Jan 29, 2009, 8:54:58 PM1/29/09
to
Majorinc Kazimir schrieb:

> Look, I've macroexpanded your macro and seen what are
> you doing. You are good programmer, no doubt. But man,
>
> > (defmacro at-least (n &rest es &aux (nsym (gensym)) (carsym (gensym)))
>> (if (null es) nil
>> `(let* ((,nsym ,n) (,carsym ,(car es)))
>> (cond ((zerop ,nsym) t)
>> ((= ,nsym 1) (or ,carsym ,@`,(cdr es)))
>> (t (at-least (if ,carsym (1- ,nsym) ,nsym) ,@`,(cdr es)))))))
>
> you have 57 tokens of dense code already, without
> redundancy. Two gensyms, ,@`, stuff - for such a simple
> and natural idea as a generalization of "or."
>
> Compare it with Newlisp -
>
> (define-macro (at-least atn)
> (let ((aten (eval atn)))
> (doargs(ati (zero? aten))
> (and (eval ati)
> (dec aten)))
> (zero? aten)))
>
> just one loop, 18 tokens, everyone can understand it.

Honestly, I look at your macro and I have no fuckin idea what the heck
is going on.
In the first line you eval atn? Why is it a macro then in the first
place? And by the way, into what code does your macro expand?
Where is the return value?
What is this ati thing doing there? Why can you (dec aten)? You don’t
know if (eval atn) will return a number?!
No really, I basically understand nothing about it. Okay, maybe just my
fault. But still, I don’t buy this “everyone can understand it” part.

When I compare your macro with my

(defmacro at-least-two [& x]
`(= 2 (count (take 2 (filter identity (lazy-cat ~@(map vector x)))))))

then I know which one I prefer.
And this is not only because my version just consists of 17 tokens and
has basically just one line of code. No, it is also because my version
also works correctly with lexical variables, can be compiled into
efficient code, has a clear return value and needs no loop, but instead
makes use of idioms of functional programming, which I regard as pretty
elegant.


André
--
Lisp is not dead. It’s just the URL that has changed:
http://clojure.org/

kazimir...@gmail.com

unread,
Jan 29, 2009, 11:18:27 PM1/29/09
to
On Jan 30, 2:54 am, André Thieme

> Honestly, I look at your macro and I have
> no fuckin idea what the heck is going on.
> In the first line you eval atn? Why is it
> a macro then in the first place? And by
> the way, into what code does your macro expand?
> Where is the return value? What is this ati
> thing doing there? Why can you (dec aten)?
> You don’t know if (eval atn) will return a number?!
> No really, I basically understand nothing about it.
> Okay, maybe just my fault. But still,
> I don’t buy this “everyone can understand it” part.

It is because you jumped over my message from
27th January, I do not talk about at-least two,
but about more general at-least n, where n can
be expression. Check the message, there is the
test case there, and if you can send your Clojure solution that passes
that test, it would be nice.

Watch out, special cases is that it should work for (at-least 0 ...)

Other thing is the concept of fexpr. They are like
functions that get unevaluated arguments. So,
if macros return code that should be evaluated
to get result, fexprs return result directly.
Fexprs are evaluated during runtime.

They were alternative to macros in early Lisps,
check Kent Pitman's article Special Forms in
Lisp. Pitman advocated macros in that article.
Hard to read.

> When I compare your macro with my
>
> (defmacro at-least-two [& x]
> `(= 2 (count (take 2 (filter identity (lazy-cat ~@(map vector x)))))))

> then I know which one I prefer.
> And this is not only because my version just
> consists of 17 tokens and has basically just
> one line of code.

> No, it is also because my version
> also works correctly with lexical variables,
> can be compiled into efficient code, has a
> clear return value and needs no loop, but instead
> makes use of idioms of functional programming,
> which I regard as pretty elegant.

I can agree that your idioms of functional programming
are elegant. But, it is not the same if you can achieve
brevity with strong, high level functions or with basic
operations. It is just different kind of expressive power.

jos...@corporate-world.lisp.de

unread,
Jan 30, 2009, 7:48:44 AM1/30/09
to

Still the name capture problem.

> And Newlisp at-least is the first class macro, which can
> be passed as argument or returned as result, applied,
> mapped, analyzed and mutated during runtime. So, I think
> I can drive my point home.

You can't. My CL macro is much clearer, less buggy,
does not need EVAL, works with a compiler
and the expanded code can be inspected easily
BEFORE running the code.

> Still, it must be that CL macros have some advantage.
> Yes they do - your CL macro can be efficiently compiled,

It can be compiled. Period. The Lisp compiler will
already check the code for syntactic correctness,
correct arglists, unknown variables, etc.

> while my Newlisp macro (fexpr) cannot. Some fexprs can
> be efficiently compiled, but not this one.
>
> And that's it, for what its worth.


Just installing Newlisp 10.

* Installer barfs, because I have a newer version installed.
That's wrong. my version was Newlisp 9. I'm installing 10.


* I can't paste a multi-line statement into the REPL????

Wow!

Pasting your example at the REPL:

> (define-macro (at-least atn)

ERR: missing parenthesis : "...(define-macro (at-least atn) "
> (let ((aten (eval atn)))

ERR: missing parenthesis : "... (let ((aten (eval atn))) "
> (doargs(ati (zero? aten))

ERR: missing parenthesis : "... (doargs(ati (zero? aten))"
> (when (eval ati)

ERR: missing parenthesis : "... (when (eval ati) "
> (dec aten)))
-1

ERR: missing parenthesis : "... (dec aten)))"
> (zero? aten)))


OOOOOPS! Cough.

* Recursive EVAL.

(define (test )
(let ((atn 1))
(at-least (eval atn) 1 1 1)))


> (test)

ERR: call stack overflow in function eval : eval
called from user defined function test


Haha, I like your code.


(define (test )
(let ((atn 1))
(at-least (eval 'atn) 1 1 1)))


Notice the quote.


> (test)

ERR: value expected in function dec : (eval 'atn)
called from user defined function test


Hehe!

* Scope is bad.


(define (my-zero what) (= 0 what))


(define-macro (my-at-least atn)
(let ((aten (eval atn)))
(doargs(ati (my-zero aten))
(when (eval ati)
(dec aten)))
(zero? aten)))

(define (test2 )
(let ((my-zero 0))
(my-at-least 3 1 1 1)))

> (test2)

ERR: illegal parameter type in function doargs : aten
called from user defined function test2

Notice the interesting error message.

Notice how scopes are bad. It calls the dynamic my-zero.
Every variable can potentially overwrite any
user defined function in any code.


* no breakloop

newLISP v.10.0.1 on OSX IPv4 UTF-8, execute 'newlisp -h' for more
info.

> (define (foo n) (/ n 0))
(lambda (n) (/ n 0))
> (foo 3)

ERR: division by zero in function /
called from user defined function foo


Notice how there is no breakloop.


I'm not overwhelmed, I have to say.


Pascal J. Bourguignon

unread,
Jan 30, 2009, 9:36:21 AM1/30/09
to
"jos...@corporate-world.lisp.de" <jos...@corporate-world.lisp.de> writes:
> * Installer barfs, because I have a newer version installed.
> * I can't paste a multi-line statement into the REPL????
> * Recursive EVAL.
> * Scope is bad.

> * no breakloop
>
> newLISP v.10.0.1 on OSX IPv4 UTF-8, execute 'newlisp -h' for more
> info.
>
>> (define (foo n) (/ n 0))
> (lambda (n) (/ n 0))
>> (foo 3)
>
> ERR: division by zero in function /
> called from user defined function foo
>
>
> Notice how there is no breakloop.

I'm not sure that's helping.

If we keep sending such "bug reports" to other language designers,
eventually, they will implement corrections, and make their language
somewhat undistinguishable from Lisp, only just annoyingly
incompatible with Common Lisp. Then we're back to the situation 25
years ago, and we'll have to make a new "Standard Lisp", that will
have nothing new with respect to Common Lisp, only standardize again
what has been standardized 25 years ago.

The message should rather be a concise: "Well, nice, but Common Lisp
is better, I'll stay with CL". And instead of spending time
explaining them why, just spare some time to further develop lisp
tools for lisp developers.

We've got a technology so advanced that it's tempting to to sleep on
our (elder's) lauriers. But if we don't make progress on our side,
the other will soon reach our level. Let's stop helping them, and
work toward strictly lisp enhancements. There's a lot to do.

--
__Pascal Bourguignon__

jos...@corporate-world.lisp.de

unread,
Jan 30, 2009, 11:59:39 AM1/30/09
to
On 30 Jan., 15:36, p...@informatimago.com (Pascal J. Bourguignon)
wrote:

I completely disagree. Programming communities are built by
communication.
Communication of solutions, communication of 'standards',
communication
of ideas. If 'we' want to communicate with developers from 'nearby'
communities, we should be able to make clear what 'we' have to offer,
why the technology is the way it is and which benefits and drawbacks
that has. Only saying 'Common Lisp' is better does not help.
We need to explain the advantages in practical terms and
demonstrate that.

Actually explaining the advantages of Common Lisp REPLs, error
handling, etc.
so that it gets copied, allows MORE people to understand the basic
technology and makes adoption EASIER.

My philosophy has always been:
"Take this beat, I don`t mind I got plenty others and they so fine."
From a rare Prince track, mid 80s.
Later in the song: "Take this beat, U know U need it"


>
> We've got a technology so advanced that it's tempting to to sleep on
> our (elder's) lauriers.  But if we don't make progress on our side,
> the other will soon reach our level.  Let's stop helping them, and
> work toward strictly lisp enhancements.  There's a lot to do.

Help growing the user community. That helps most.

>
> --
> __Pascal Bourguignon__

Raffael Cavallaro

unread,
Jan 30, 2009, 1:00:46 PM1/30/09
to
On 2009-01-29 20:02:15 -0500, Majorinc Kazimir <fa...@email.address> said:

> Two gensyms, ,@`, stuff - for such a simple
> and natural idea as a generalization of "or."

Your semantics for at-least are *not* a generalization of or because or
*always* evaluates it's first arg but now you are complaining that my
version of at-least does the same as or does.

If it were a generalization of or then Rainer's macro would suffice to
give your semantics but Rainer's version does not give your semantics
because it returns nil for (at-least 0 nil). This is no fault of
Rainer's - he was simply giving you a generalized or, and your
semantics of at-least are *not* the same as or.

You've changed the semantics again, but this is simply a matter of
conditionalizing the evaluation of the first arg on the value of n:

(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))))))))

You've changed the semantics twice and I've complied twice. I was able
to do this and to correct bugs because I am able to macroexpand my
version to see what code it will generate for test cases. As Rainer has
pointed out, your newlisp macro is *not* equivalent to this because you
still have the name capture problem, and you can neither see the
macroexpansion before running the code nor compile it.

regards,

André Thieme

unread,
Jan 30, 2009, 2:49:21 PM1/30/09
to
kazimir...@gmail.com schrieb:

> On Jan 30, 2:54 am, André Thieme
>
>> Honestly, I look at your macro and I have
>> no fuckin idea what the heck is going on.
>> In the first line you eval atn? Why is it
>> a macro then in the first place? And by
>> the way, into what code does your macro expand?
>> Where is the return value? What is this ati
>> thing doing there? Why can you (dec aten)?
>> You don’t know if (eval atn) will return a number?!
>> No really, I basically understand nothing about it.
>> Okay, maybe just my fault. But still,
>> I don’t buy this “everyone can understand it” part.
>
> It is because you jumped over my message from
> 27th January, I do not talk about at-least two,
> but about more general at-least n, where n can
> be expression. Check the message, there is the
> test case there, and if you can send your Clojure solution that passes
> that test, it would be nice.

Sure, I will attach it to this message.


> Watch out, special cases is that it should work for (at-least 0 ...)

Ok.


> Other thing is the concept of fexpr. They are like
> functions that get unevaluated arguments. So,
> if macros return code that should be evaluated
> to get result, fexprs return result directly.
> Fexprs are evaluated during runtime.

Well, in CL it is the same. Macros are evaluated during runtime.
As long Lisp is not running you can’t expand your macros.
But yes, I know what you mean ;-)
Although I really can’t see why this could be interesting.
Passing them around is nothing more but syntactical sugar.
One can always wrap the call to the macro into an anon function.
It’s so simple in Clojure.
lambda is called fn there. And there is a reader macro for the
most typical cases.
(fn [arg1 arg2 arg3] (and arg1 arg2 arg3)) becomes:
#(and %1 %2 %3)

If I want to evaluate things at runtime, then why not use functions?
Could you please present me a few pratical examples where it is helpful
to have a macro running “at runtime”?

>> (defmacro at-least-two [& x]
>> `(= 2 (count (take 2 (filter identity (lazy-cat ~@(map vector x)))))))

Okay, here the modifications to make it work for any natural number:
(defmacro at-least [n & x]
`(= ~n (count (take ~n (filter identity (lazy-cat ~@(map vector x)))))))


user> (let [x 1, y 2, z 3, n 3]
(println (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))))))
--> prints false


user> (let [x 1, y 2, z 3, n 3]
(println (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 100))))))
--> prints true


(at-least 0 nil) ==> true

I don’t know if these were your test cases.


> I can agree that your idioms of functional programming
> are elegant. But, it is not the same if you can achieve
> brevity with strong, high level functions or with basic
> operations. It is just different kind of expressive power.

Honestly, what I did in the macro is old stuff in Clojure.
Those are the primitive tools, very basic.
Could you please give some examples of expressive power that you miss?

Kaz Kylheku

unread,
Jan 30, 2009, 3:08:47 PM1/30/09
to

It's not; it's something called an ``frexp'' in ancient Lisp-speak. A better
name for it would be ``interpreter function''. Whereas macros extend the
compiler with new code transformations that can implement new special forms,
frexps extend the interpreter with new evalution logic to implement new
evaluation for special forms.

Why at-least has to evaluate ATN is because that's exactly what the
Lisp interpreter would have to do if at-least was a special form
intrinsic to the interpreter.

One problem with the approach is that there is no environment parameter.

If you were designing an interpreter, all your functions for handling special
forms would have an extra parameter ``env'' which would be explicitly used to
resolve variable references. An extension mechansim for doing the same thing
would also have to have an environment parameter. This is really not optional.
In macros you don't need one because you are sticking the expanded code into
the proper context, where it is evaluated later. No frexp mechanism can be
sound without environment passing, even if your language only has dynamic
scope.

The above exposes the evaluation of atn and other forms to the visibility of
the frexps's own internal variables.

Suppose you have:

(let ((atn 3)) (at-least atn ..))

The frexp will try to eval the form ATN, which of course is a symbol
that has a binding within the frexp itself, to the symbol 'ATN.

You should see Majorinc's re-implementation of IF that doesn't use IF. What it
does is evaluate a form to yield either TRUE or FALSE. it then treats these
symbols as strings and takes their length! The length of TRUE is 4, and the
length of FALSE is 5. If you subtract 3, you get 1 for true, 2 for false. Of
course these are the very positions of the consequent and alternate clauses in
the if form!

du...@franz.com

unread,
Jan 30, 2009, 3:23:35 PM1/30/09
to
On Jan 30, 12:08 pm, Kaz Kylheku <kkylh...@gmail.com> wrote:
> It's not; it's something called an ``frexp'' in ancient Lisp-speak.

Minor nit: I think you mean fexpr, not frexp.

Duane

jos...@corporate-world.lisp.de

unread,
Jan 30, 2009, 4:04:04 PM1/30/09
to
On 30 Jan., 21:08, Kaz Kylheku <kkylh...@gmail.com> wrote:

...

> If you were designing an interpreter, all your functions for handling special
> forms would have an extra parameter ``env'' which would be explicitly used to
> resolve variable references. An extension mechansim for doing the same thing
> would also have to have an environment parameter.  This is really not optional.
> In macros you don't need one because you are sticking the expanded code into
> the proper context, where it is evaluated later.

Right, there is no need to deal with environments in the generated
code.
The macro itself might want to deal with environments.

See for example &environment in macro lambda lists:

'&environment is followed by a single variable that is bound to an
environment representing the lexical environment in which the macro
call is to be interpreted. This environment should be used with
macro-function, get-setf-expansion, compiler-macro-function,
and macroexpand (for example) in computing the expansion of the
macro, to ensure that any lexical bindings or definitions
established in the compilation environment are taken into account.
&environment can only appear at the top level of a macro lambda
list, and can only appear once, but can appear anywhere in that
list; the &environment parameter is bound along with &whole
before any other variables in the lambda list, regardless of where
&environment appears in the lambda list. The object that is bound
to the environment parameter has dynamic extent.'

...

André Thieme

unread,
Jan 30, 2009, 5:04:20 PM1/30/09
to
Kaz Kylheku schrieb:

> On 2009-01-30, André Thieme <address.good.un...@justmail.de> wrote:
>> Majorinc Kazimir schrieb:

Thanks for the explanations Kaz.


> You should see Majorinc's re-implementation of IF that doesn't use IF. What it
> does is evaluate a form to yield either TRUE or FALSE. it then treats these
> symbols as strings and takes their length! The length of TRUE is 4, and the
> length of FALSE is 5. If you subtract 3, you get 1 for true, 2 for false. Of
> course these are the very positions of the consequent and alternate clauses in
> the if form!

I am under the impression that f-expressions can make people very
creative. Shocking and funny at the same time.

Raffael Cavallaro

unread,
Jan 30, 2009, 5:35:02 PM1/30/09
to
On 2009-01-30 14:49:21 -0500, André Thieme
<address.good.un...@justmail.de> said:

> Okay, here the modifications to make it work for any natural number:
> (defmacro at-least [n & x]
> `(= ~n (count (take ~n (filter identity (lazy-cat ~@(map vector x)))))))

This has an unintended evaluation:

user=>(defmacro at-least [n & x]


`(= ~n (count (take ~n (filter identity (lazy-cat ~@(map vector x)))))))

nil
user=> (at-least 0 (print 'shouldn't-see-this))
shouldn t-see-thistrue
user=>
--
Raffael Cavallaro, Ph.D.

André Thieme

unread,
Jan 30, 2009, 7:03:41 PM1/30/09
to
Raffael Cavallaro schrieb:

Yes, well spotted.
The issue here is lazy-cat.
I did not notice this behaviour before. I asked in #Clojure and someone
mentioned this short article:
http://w01fe.com/blog/2008/12/clojure-and-lazy-seqs/

So I personally see this current behaviour as a bug in how seqs are treated.

Majorinc Kazimir

unread,
Jan 30, 2009, 7:52:08 PM1/30/09
to

>>
>> (define-macro (at-least atn)
>> (let ((aten (eval atn)))
>> (doargs(ati (zero? aten))
>> (and (eval ati)
>> (dec aten)))
>> (zero? aten)))
>>
>
> Still the name capture problem.

Not yet.
But yes if you have so much code that you forget variables.

For that case one can use Newlisp lexical scope feature
"context," or one of the techniques I already described
few times in this thread, including in very first post.

The simplest naming convention replacing n with at-least-n
works for all examples you described here as errors. I
already posted that code. It is same code as this one
with different variable names.

Same answer holds for all examples you gave here.

>> And Newlisp at-least is the first class macro, which can
>> be passed as argument or returned as result, applied,
>> mapped, analyzed and mutated during runtime. So, I think
>> I can drive my point home.
>
> You can't. My CL macro is much clearer, less buggy,
> does not need EVAL, works with a compiler
> and the expanded code can be inspected easily
> BEFORE running the code.

Please, Rainier.

This is your code:

(defmacro at-least (n &rest es &aux (c (gensym)))
`(let ((,c 0))
,@(loop for e in es collect `(when ,e (incf ,c)))
(>= ,c ,n)))

* You use advanced techniques like gensym, `,@, I do not.

* your code has 32 tokens, my 18.

* your macro expands, fexprs do not expand, they
are evaluated as they are. Like functions.

* neither one of these is honestly "buggy", neither
my nor yours.

* macro is not the first class citizen, fexpr is.

I mean, be realistic. I honestly said when CL macros are
better - if you insist on compiling.

>> Still, it must be that CL macros have some advantage.
>> Yes they do - your CL macro can be efficiently compiled,
>
> It can be compiled. Period. The Lisp compiler will
> already check the code for syntactic correctness,
> correct arglists, unknown variables, etc.

True. For what it is worth.

Majorinc Kazimir

unread,
Jan 30, 2009, 8:49:07 PM1/30/09
to
Raffael Cavallaro wrote:

> (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))))))))

Thanx for your help.


jos...@corporate-world.lisp.de

unread,
Jan 30, 2009, 9:29:31 PM1/30/09
to
On Jan 31, 1:52 am, Majorinc Kazimir <fa...@email.address> wrote:
> >> (define-macro (at-least atn)
> >>        (let ((aten (eval atn)))
> >>             (doargs(ati (zero? aten))
> >>                    (and (eval ati)
> >>                         (dec aten)))
> >>             (zero? aten)))
>
> > Still the name capture problem.
>
> Not yet.
> But yes if you have so much code that you forget variables.

Or if you use libraries of code.

> For that case one can use Newlisp lexical scope feature
> "context," or one of the techniques I already described
> few times in this thread, including in very first post.

You have described it, but you have avoided it in your code
examples. All the code you have posted in this thread
inherits the scoping problems of Newlisp and the name
capture problems.

Why not post code that does not have these problems,
when Newlisp is (?) capable of avoiding it?

Maybe your token count would increase then?

> The simplest naming convention replacing n with at-least-n
> works for all examples you described here as errors. I
> already posted that code. It is same code as this one
> with different variable names.
>
> Same answer holds for all examples you gave here.

I've given you the example where a LET can
overwrite any function in a code that's been called.
You would need to introduce a naming convention
that separates ALL function names from variable names,
since you can't know which functions gets called
unless you inspect all source code.

>   >> And Newlisp at-least is the first class macro, which can
>
> >> be passed as argument or returned as result, applied,
> >> mapped, analyzed and mutated during runtime. So, I think
> >> I can drive my point home.
>
> > You can't. My CL macro is much clearer, less buggy,
> > does not need EVAL, works with a compiler
> > and the expanded code can be inspected easily
> > BEFORE running the code.
>
> Please, Rainier.

Rainer. Not Rainier.

>
> This is your code:
>
> (defmacro at-least (n &rest es &aux (c (gensym)))
>    `(let ((,c 0))
>       ,@(loop for e in es collect `(when ,e (incf ,c)))
>       (>= ,c ,n)))
>
> * You use advanced techniques like gensym, `,@, I do not.

What is advanced about GENSYM? It just generates a fresh
unique symbol. I could have cheated and could have presented this:

(defmacro at-least (n &rest es)
`(let ((c 0))
,@(loop for e in es collect `(when ,e (incf c)))
(>= c ,n)))

But I did not. Cleaner code is more important than less tokens.

You are using EVAL, which is much more complicated than
ANYTHING in my code. Behind EVAL is a complete
interpreter, with all kinds of fancy interaction
that can only be understood by looking at all
the using code and watching it execute.

My code is using a simple code template,
where some code gets spliced in from a loop using another template.

Template
LOOP Template

How is that difficult?

> * your code has 32 tokens, my 18.
>
> * your macro expands, fexprs do not expand, they
>    are evaluated as they are. Like functions.

No, FEXPRs get their arguments unevaluated. UNLIKE other functions.
My macro expands - that's a feature.

> * neither one of these is honestly "buggy", neither
>    my nor yours.

Newlisp is full of scoping problems. The code
you have posted inherits that. I've shown
you some ugly examples.

> * macro is not the first class citizen, fexpr is.

Given the scope problems of Newlisp, it would
be better if you could not pass FEXPRs around.
Actually the only way to get rid of all that
is to introduce lexical binding by default
and remove FEXPRs from the language. That's
what Scheme and Common Lisp did, when they
modernized Lisp.

> I mean, be realistic. I honestly said when CL macros are
> better - if you insist on compiling.
>
> >> Still, it must be that CL macros have some advantage.
> >> Yes they do - your CL macro can be efficiently compiled,
>
> > It can be compiled. Period. The Lisp compiler will
> > already check the code for syntactic correctness,
> > correct arglists, unknown variables, etc.
>
> True. For what it is worth.

It is worth quite a lot. Newlisp can't do this:

? (defun test () (let ((fool 'barl)) (at-least 2 (sin 3 4) foo)))
;Compiler warnings :
; Function call arguments don't match current definition of SIN, in
TEST.
; Undeclared free variable FOO, in TEST.
; Unused lexical variable FOOL, in TEST.
TEST

Newlisp won't detect arglist errors, it won't detect unused variables,
it won't detect undeclared free variables, and so on. You can
run it and see if it works or not.

Since AT-LEAST is an FEXPR in Newlisp, it is also hopeless do to so,
even if there would be a compiler. Since the FEXPR
gets data, you can write anything and the compiler
would have no chance to analyze it.

You have posted a challenge. Several people have responded and shown
you
how AT-LEAST can be easily implemented in a way that is
working quite nicely for all practical purposes and
comes in with some added bonus (like less scope problems,
support for compilation, etc.).

You wanted some additional flexibility for your programs. Flexibility
that is provided by the evaluation model of Newlisp. So far
I have seen nothing really convincing (like actual code
that maps AT-LEAST over something) in this thread that
makes me want to use that evaluation model. Do you have
other 'needs' for it? How about something that's an actual
domain problem and not just a language construct problem.


Kaz Kylheku

unread,
Feb 1, 2009, 12:08:29 AM2/1/09
to
On 2009-01-31, Majorinc Kazimir <fa...@email.address> wrote:
>
>>>
>>> (define-macro (at-least atn)
>>> (let ((aten (eval atn)))
>>> (doargs(ati (zero? aten))
>>> (and (eval ati)
>>> (dec aten)))
>>> (zero? aten)))
>>>
>>
>> Still the name capture problem.
>
> Not yet.
> But yes if you have so much code that you forget variables.
>
> For that case one can use Newlisp lexical scope feature
> "context," or one of the techniques I already described
> few times in this thread, including in very first post.

I.e. when you want to get the semanics 100% right, the NewLisp
convenience sort of runs off a cliff.

NewLisp contexts are ugly garbage compared to lexical closures.

> The simplest naming convention replacing n with at-least-n
> works for all examples you described here as errors.

It doesn't work for the example where the evaluated forms
contain the symbol at-least-n.

I
> already posted that code. It is same code as this one
> with different variable names.
>
> Same answer holds for all examples you gave here.
>
>
>
> >> And Newlisp at-least is the first class macro, which can
>>> be passed as argument or returned as result, applied,
>>> mapped, analyzed and mutated during runtime. So, I think
>>> I can drive my point home.
>>
>> You can't. My CL macro is much clearer, less buggy,
>> does not need EVAL, works with a compiler
>> and the expanded code can be inspected easily
>> BEFORE running the code.
>
> Please, Rainier.
>
> This is your code:
>
> (defmacro at-least (n &rest es &aux (c (gensym)))
> `(let ((,c 0))
> ,@(loop for e in es collect `(when ,e (incf ,c)))
> (>= ,c ,n)))
>
> * You use advanced techniques like gensym, `,@, I do not.

That's because this code takes care of referential transparency issues, whereas
yours does not.

The above macro could also use an idiotic approach instead of the gensym:

(defmacro at-least (n &rest es)

`(let ((at-least-c 0))
,@(loop for e in es collect `(when ,e (incf at-least-c)))
(>= at-least-c ,n)))

See? No more gensym. Nobody will ever use AT-LEAST-C in their code.

``Worse is better'', right?

> * your code has 32 tokens, my 18.

It has fewer tokens now that we've taken out the gensym.

The NewLisp fexpr-defining facility has a looping construct which
gives implicit access to trailing arguments.

What if trailing arguments are not allowed? Yu have to write explicit
echecks.

In Lisp we say ``&rest args'' or ``&body args''. If we don't, then
the macro isn't variadic; passing too many arguments results in a
diagnosis that we don't have to implement ourselves.

LOOP FOR ... IN ... COLLECT is verbose, but very clear.

> * your macro expands, fexprs do not expand, they
> are evaluated as they are. Like functions.
>
> * neither one of these is honestly "buggy", neither
> my nor yours.

Yours is buggy due to lack of referential transparency.

This stupid bugginess could have been fixed by providing
fexprs with environment parameters.

Any proper Lisp hacker with two functioning brain hemispheres
would have implemented it that way:

(define-fexpr (environment arg ...)
(let ((evaled-arg (eval arg environment)))
...)))

Throughout the functions that maek up an interpreter, you must
pass around the environment. If the environment is implicit,
then there must somehow exist a separation between the
interpreter's environment and the program's environment.

Any entanglement between the interpreter's environment
and a program's environment is a bug.

It's not just your bug, but a bug in the programming language.

It is Broken By Design.

> * macro is not the first class citizen, fexpr is.
>
> I mean, be realistic. I honestly said when CL macros are
> better - if you insist on compiling.

CL macros are better because they are good examples of macros.

A good implementation of macros is better than a lousy implementation
of fexprs.

Kaz Kylheku

unread,
Feb 1, 2009, 12:12:57 AM2/1/09
to
On 2009-01-31, Majorinc Kazimir <fa...@email.address> wrote:
>
>>>
>>> (define-macro (at-least atn)
>>> (let ((aten (eval atn)))
>>> (doargs(ati (zero? aten))
>>> (and (eval ati)
>>> (dec aten)))
>>> (zero? aten)))
>>>
>>
>> Still the name capture problem.
>
> Not yet.
> But yes if you have so much code that you forget variables.

I work in a company where the codebase is worked on by a hundred developers and
takes more than an hour to compile on a fast build machine with eight CPU
cores.

It's not Lisp, but it has variables, and there is so much of it that it's
way past the size that you can keep all of the variables in your head.

Welcome to the software world outside your bedroom.

Marco Antoniotti

unread,
Feb 1, 2009, 5:32:06 PM2/1/09
to
On Jan 23, 9:32 pm, Majorinc Kazimir <fa...@email.address> wrote:
> ;============================
>
> Pascal:
>
> But where is the paper that argues that fexprs are sometimes
> better than macros because they are more flexible and
> powerful than macros? Or easier to use, learn, etc? It sounds
> like Mr.  Majorinc is confident enough that he should be
> able to set about writing that paper. I'm looking forward to it!
>
> ;============================
>
> I define macro (at-least-two arg1 ... argn)
> that returns true if and only if at least
> two of args evaluate to the true. Macro is
> needed because we want lazy evaluation
> of arg1 to argn, like in OR or AND.
>
> Newlisp (17 tokens, very simple)
>
>      (define-macro (at-least-two)
>               (let (c)
>                    (doargs (i (= c 2))
>                            (if (eval i)
>                                (inc c)))
>                    (>= c 2)))
>
>      ; test
>
>      (println (at-least-two (= 1 1)
>                             (= 3 2)
>                             (= 2 5)
>                             (= 2 20)))
>
>      (exit)
>
> (A) Could you write simpler, more elegant one in
>      your favorite Lisp?
>
> Only solution so far is Rainer Joswig's CL
>
>      (defmacro at-least-two (&rest expressions)
>        (let ((c (gensym)))
>          `(let ((,c 0))
>             (block nil
>               ,@(loop for expression in expressions
>                       collect `(when ,expression (incf ,c))
>                       collect `(when (= ,c 2) (return t)))
>               nil))))
>
> It has 38 tokens, and I think it is significantly more
> complicated (and not the first class.)
>
> ------------------------------------------------
>
> (B) Fexprs are frequently described as "dangerous."
>      Practice of Newlispers seems to be different:
>      there are no complains. Probably because Newlispers
>      typically use lexical / static scope constructs
>      named "contexts."
>
>      However, even without lexical scope, I believe
>      that fexprs are pretty safe.
>
> (B1) Is anyone able to find the expression that
>       breaks at-least-two macro?
>
> (B2) Is anyone able to find the expression that
>       breaks at-least-two macro after trivial name
>       mangling, for example, replacing i with
>       at-least-two-i.
>
> (B3) Is anyone able to find the expression that
>       breaks at-least-two macro after single
>       application of my function protect2 from
>       my Newlisp librarywww.instprog.com,
>       explained at my blog kazimirmajorinc.blogspot.com,
>       post "Don't fear Dynamic Scope (2)"
>
> ------------------------------------------------
> (C) I define more general macro at-least that should
> accept expressions of the following kind:
>
> (at-least 3 (= 1 1) (= 2 2) (= 4 4))
> (at-least 7 (= 1 1) (= 7 2) ... )
>
> Solution in Newlisp
>
> (define-macro (at-least n)
>           (let (c)
>                (doargs (i (= c n))
>                        (if (eval i)
>                            (inc c)))
>                (>= c n)))
>
> Could you write simpler, more elegant one in
> your favorite Lisp?

Ok. I'll jump in. But I will cheat and will put in a shameless
plug :)

(use-package "CLAZY") ; http://common-lisp.net/project/clazy

(deflazy at-least-n-f (n &rest args)
(cond ((zerop n) t)
((null args) nil)
((force (first args)) ; I hate this FORCE!
(apply 'at-least-n (1- n) (rest args)))
(t (apply 'at-least-n n (rest args)))))

(defmacro at-least-n (n &rest args)
`(lazy:call 'at-least-n-f ,n .,args)) ; Pkg qualifier just for
emphasis.

cl-prompt> (at-least-n 2 nil nil t nil t nil (loop))
T

Cheers
--
Marco
www.european-lisp-symposium.org


Raffael Cavallaro

unread,
Feb 2, 2009, 12:43:38 AM2/2/09
to
On 2009-02-01 17:32:06 -0500, Marco Antoniotti <mar...@gmail.com> said:

> Ok. I'll jump in. But I will cheat and will put in a shameless
> plug :)
>
> (use-package "CLAZY") ; http://common-lisp.net/project/clazy

Marco, you clazy man!

;^)
--
Raffael Cavallaro, Ph.D.

Marco Antoniotti

unread,
Feb 2, 2009, 11:17:24 AM2/2/09
to
On Feb 2, 6:43 am, Raffael Cavallaro
<raffaelcavall...@pas.espam.s.il.vous.plait.mac.com> wrote:

> On 2009-02-01 17:32:06 -0500, Marco Antoniotti <marc...@gmail.com> said:
>
> > Ok.  I'll jump in.  But I will cheat and will put in a shameless
> > plug :)
>
> > (use-package "CLAZY") ;http://common-lisp.net/project/clazy
>
> Marco, you clazy man!
>
> ;^)
> --
> Raffael Cavallaro, Ph.D.

Not nearly as cRazy as trying to post the equivalent (and I stress
'equivalent') version in Ruby :)

Cheers
--
Marco


Marco Antoniotti

unread,
Feb 3, 2009, 4:08:08 AM2/3/09
to

What??? No Ruby version yet?

Cheers
--
Marco

0 new messages