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

Newbie performance question

83 views
Skip to first unread message

dgiaimo

unread,
Nov 9, 2010, 1:34:58 PM11/9/10
to
Hi all,

I am fairly new to Lisp programming and am trying to get a sense of
how to write code performantly. Are there good references online for
general tips in this regard? Also, for a particular example, can
someone tell me why the Lisp code below is so much slower than the C
code that follows it? They are both implementations of the sieve of
Eratosthenes. As far as I understand, they should be doing the same
thing, but the C code runs in around 30 seconds without any
optimization set on the compiler whereas the Lisp code takes half a
day. Is this just due to the fact that the Lisp is interpreted, or am
I missing something important?

;;; Lisp CODE

(format t "Finding primes.~%")

(defvar *memo* (make-array 1000000001 :element-type 'bit :initial-
element 0))

(setf (aref *memo* 0) 1
(aref *memo* 1) 1)

(loop for i from 2 to (sqrt 1000000000)
when (= (aref *memo* i) 0)
do (loop for j from (* i i) to 1000000000 by i
do (setf (aref *memo* j) 1
)
)
)

// C CODE

#include <ctype.h>
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char** argv)
{
int max = 1000000000;

char* nums = malloc(max + 1);

memset(nums, 0, max+1);

nums[0] = 1;
nums[1] = 1;

printf("Finding primes.\n");

for(int i=2; i<=sqrt(max); i++)
{
if(!nums[i])
{
for(int64_t j=((int64_t)i)*((int64_t)i); j<=max; j+=i)
{
nums[j] = 1;
}
}
}

return EXIT_SUCCESS;
}

Thanks,
Dan Giaimo

jos...@lisp.de

unread,
Nov 9, 2010, 2:12:15 PM11/9/10
to

* you need to compile the code

* you need to use a Lisp that compiles to fast code, means usually
that it generates native code

* some implementations can't handle such large arrays
see the variable ARRAY-TOTAL-SIZE-LIMIT

* the Lisp code does not the same as the C code.
Arithmetic in Lisp and C may look similar: (+ a b) and a + b
But in reality these are very different operations.
Lisp arithmetic is generic with automatic overflow to bignums.
It also supports ratios and complex numbers.
If you want to get limited arithmetic, you need to declare that
and in many cases the compiler will generate the same
unsafe code (with silent overflows, etc.) as in C.

Zach Beane

unread,
Nov 9, 2010, 2:14:13 PM11/9/10
to
dgiaimo <dgi...@gmail.com> writes:

> Hi all,
>
> I am fairly new to Lisp programming and am trying to get a sense of
> how to write code performantly. Are there good references online for
> general tips in this regard? Also, for a particular example, can
> someone tell me why the Lisp code below is so much slower than the C
> code that follows it? They are both implementations of the sieve of
> Eratosthenes. As far as I understand, they should be doing the same
> thing, but the C code runs in around 30 seconds without any
> optimization set on the compiler whereas the Lisp code takes half a
> day. Is this just due to the fact that the Lisp is interpreted, or am
> I missing something important?

Special variable access can be slower than lexical variable access. And
compiling the code usually makes it run much faster. I timed this
variation of your code:

(defun primes ()


(format t "Finding primes.~%")

(let ((memo (make-array 1000000001 :element-type 'bit
:initial-element 0)))
(setf (sbit memo 0) 1
(sbit memo 1) 1)


(loop for i from 2 to (sqrt 1000000000)

when (= (sbit memo i) 0)


do (loop for j from (* i i) to 1000000000 by i

do (setf (sbit memo j) 1))))
t)

It runs in about 45 seconds on my laptop with SBCL. SBCL compiles
everything by default, and produces pretty efficient bit-vector code.

Zach

dgiaimo

unread,
Nov 9, 2010, 2:25:45 PM11/9/10
to
On Nov 9, 2:14 pm, Zach Beane <x...@xach.com> wrote:

Using sbcl, even on the code I wrote unchanged helped a lot. I had
been using clisp before. I guess it must not compile the code by
default. Thanks for the pointers.

Thanks a lot,
Dan Giaimo

Thomas A. Russ

unread,
Nov 9, 2010, 6:31:16 PM11/9/10
to
dgiaimo <dgi...@gmail.com> writes:

Compiling often makes a huge difference. Fortunately this is easy to
do, since with a function like PRIMES, you can just do

(compile 'primes)

and then you have compiled code that you can invoke right from the
interactive lisp interface.

Trying this on my machine I get the following. I am using a 64 bit
version of SBCL and CCL, so this all fits in fixnums.

SBCL: Finding primes.
Evaluation took:
33.194 seconds of real time
29.314178 seconds of total run time
(28.651712 user, 0.662466 system)

CLISP: Exceeded fixnum size / array limit size. (24 bits)

CCL: Finding primes.
(PRIMES) took 33.612617 seconds to run
with 2 available CPU cores.
During that period, 29.648820 seconds in user mode
0.695922 seconds in system mode
0.004193 seconds was spent in GC.


These were done with a slightly modified version of Zach's code, adding
type declarations to the loop variables and compiler optimization
settings of (speed 3) (safety 0) (debug 0) (space 0). Interestingly,
this only helped a little in SBCL (5% faster) but made a big difference
for CCL twice as fast.

--
Thomas A. Russ, USC/Information Sciences Institute

Andy Venikov

unread,
Nov 10, 2010, 9:19:06 AM11/10/10
to
On 11/9/2010 6:31 PM, Thomas A. Russ wrote:

> These were done with a slightly modified version of Zach's code, adding
> type declarations to the loop variables and compiler optimization
> settings of (speed 3) (safety 0) (debug 0) (space 0).

Could you post the complete code?
I'm also coming from performance critical point of view and sometimes
worry about Lisp performance. I'd like to learn when and how to add the
type declarations to make things run faster.

Thanks,
Andy.

Thomas A. Russ

unread,
Nov 10, 2010, 1:30:05 PM11/10/10
to
Andy Venikov <swojch...@gmail.com> writes:


(defun primes ()
(declare (optimize (speed 3) (safety 0) (debug 0) (space 0)))


(format t "Finding primes.~%")
(let ((memo (make-array 1000000001 :element-type 'bit
:initial-element 0)))
(setf (sbit memo 0) 1
(sbit memo 1) 1)

(loop for i fixnum from 2 to (isqrt 1000000000)


when (= (sbit memo i) 0)

do (loop for j fixnum from (the fixnum (* i i)) to 1000000000 by i


do (setf (sbit memo j) 1)))

;; Note, this should probably return MEMO, but for
;; algorithm performance testing, we return T to
;; avoid having the REPL print a big array.
t))

Peter Keller

unread,
Nov 10, 2010, 2:54:10 PM11/10/10
to

1000000000 is a fixnum? Seems kinda large.... I think j won't be the thing
you think it is.

-pete

Zach Beane

unread,
Nov 10, 2010, 2:57:12 PM11/10/10
to
Peter Keller <psi...@cs.wisc.edu> writes:

> 1000000000 is a fixnum? Seems kinda large.... I think j won't be the thing
> you think it is.

30 bits not a fixnum? Here's a nickel, kid, get yourself an AMD64!

Zach


Peter Keller

unread,
Nov 10, 2010, 3:24:07 PM11/10/10
to

Hrm, indeed the log2 of 1000000000 is < 30 bits. My mistake.

However, why does SBCL tell me this:

* (defun primes ()


(declare (optimize (speed 3) (safety 0) (debug 0) (space 0)))
(format t "Finding primes.~%")
(let ((memo (make-array 1000000001 :element-type 'bit
:initial-element 0)))
(setf (sbit memo 0) 1
(sbit memo 1) 1)
(loop for i fixnum from 2 to (isqrt 1000000000)
when (= (sbit memo i) 0)
do (loop for j fixnum from (the fixnum (* i i)) to 1000000000 by i
do (setf (sbit memo j) 1)))

;; Note, this should probably return MEMO, but for
;; algorithm performance testing, we return T to
;; avoid having the REPL print a big array.
t))

; in: LAMBDA NIL
; (LOOP FOR J FIXNUM FROM (THE FIXNUM (* I I)) TO 1000000000 BY I
; DO (SETF (SBIT MEMO J) 1))
;
; caught WARNING:
; (in macroexpansion of (LOOP FOR J ...))
; (hint: For more precise location, try *BREAK-ON-SIGNALS*.)
; The form 1000000000 evaluated to 1000000000, which was not of the anticipated
; type (AND FIXNUM REAL).
; current LOOP context: FOR J FIXNUM FROM (THE FIXNUM
; (* I I)) TO 1000000000 BY I DO.

; (MAKE-ARRAY 1000000001 :ELEMENT-TYPE 'BIT :INITIAL-ELEMENT 0)
;
; caught WARNING:
; Asserted type (OR (MOD 536870909) CONS NULL) conflicts with derived type
; (VALUES (INTEGER 1000000001 1000000001) &OPTIONAL).
; See also:
; The SBCL Manual, Node "Handling of Types"
;
; compilation unit finished
; caught 2 WARNING conditions

PRIMES
*

-pete

Pascal J. Bourguignon

unread,
Nov 10, 2010, 3:29:29 PM11/10/10
to
Zach Beane <xa...@xach.com> writes:

Actually, I'd be more concerned about ARRAY-TOTAL-SIZE-LIMIT. An
implementation very speed conscious could set it to 1024 to be able to
keep arrays inside a few L1-data cache lines.

--
__Pascal Bourguignon__ http://www.informatimago.com/
A bad day in () is better than a good day in {}.

Andy Venikov

unread,
Nov 10, 2010, 4:37:22 PM11/10/10
to

:initial-element 0))))


Gives the following error in SBCL on x86-64:

* (let ((memo (make-array 1000000001 :element-type 'bit :initial-element
0))))
; in: LAMBDA NIL


; (MAKE-ARRAY 1000000001 :ELEMENT-TYPE 'BIT :INITIAL-ELEMENT 0)

; --> LOCALLY
; ==>


; (MAKE-ARRAY '1000000001 :ELEMENT-TYPE 'BIT :INITIAL-ELEMENT 0)
;
; caught WARNING:
; Asserted type (OR (MOD 536870909) CONS NULL) conflicts with derived type
; (VALUES (INTEGER 1000000001 1000000001) &OPTIONAL).
; See also:
; The SBCL Manual, Node "Handling of Types"
;
; compilation unit finished

; caught 1 WARNING condition

debugger invoked on a TYPE-ERROR in thread #<THREAD "initial thread" RUNNING
{A9F5909}>:
The value 1000000001 is not of type (OR (MOD 536870909) CONS NULL).

Type HELP for debugger help, or (SB-EXT:QUIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
0: [ABORT] Exit debugger, returning to top level.

(SB-C::%COMPILE-TIME-TYPE-ERROR
(1000000001)


(OR (MOD 536870909) CONS NULL)

#<unavailable argument>)


What's the deal?

Bob Felts

unread,
Nov 10, 2010, 4:45:09 PM11/10/10
to
Peter Keller <psi...@cs.wisc.edu> wrote:

> Zach Beane <xa...@xach.com> wrote:
> > Peter Keller <psi...@cs.wisc.edu> writes:
> >
> >> 1000000000 is a fixnum? Seems kinda large.... I think j won't be the thing
> >> you think it is.
> >
> > 30 bits not a fixnum? Here's a nickel, kid, get yourself an AMD64!
>
> Hrm, indeed the log2 of 1000000000 is < 30 bits. My mistake.
>
> However, why does SBCL tell me this:
>

What version of SBCL and on what system? With SBCL 1.0.44 on a MacBook
Pro runing Mac OS X 10.6.4 the code compiles and runs without error.

; compiling file "..." (written 10 NOV 2010 04:41:51 PM):

; ....fasl written
; compilation finished in 0:00:00.118

CL-USER> (time (primes))
Finding primes.
Evaluation took:
34.438 seconds of real time
34.424709 seconds of total run time (34.234002 user, 0.190707 system)
99.96% CPU
80,158,204,014 processor cycles
125,004,080 bytes consed

Andy Venikov

unread,
Nov 10, 2010, 5:03:09 PM11/10/10
to
On 11/10/2010 4:37 PM, Andy Venikov wrote:

>
> (let ((memo (make-array 1000000001 :element-type 'bit
> :initial-element 0))))
>
>
> Gives the following error in SBCL on x86-64:

<snip>

>
> (SB-C::%COMPILE-TIME-TYPE-ERROR
> (1000000001)
> (OR (MOD 536870909) CONS NULL)
> #<unavailable argument>)
>
>
> What's the deal?

Ok, it looks like "array-dimension-limit" is 536870909.
SBCL 1.0.43, x86-64, SUSE 11.1

Peter Keller

unread,
Nov 10, 2010, 5:27:32 PM11/10/10
to
Bob Felts <wr...@stablecross.com> wrote:
> Peter Keller <psi...@cs.wisc.edu> wrote:
>
>> Zach Beane <xa...@xach.com> wrote:
>> > Peter Keller <psi...@cs.wisc.edu> writes:
>> >
>> >> 1000000000 is a fixnum? Seems kinda large.... I think j won't be the thing
>> >> you think it is.
>> >
>> > 30 bits not a fixnum? Here's a nickel, kid, get yourself an AMD64!
>>
>> Hrm, indeed the log2 of 1000000000 is < 30 bits. My mistake.
>>
>> However, why does SBCL tell me this:
>>
>
> What version of SBCL and on what system? With SBCL 1.0.44 on a MacBook
> Pro runing Mac OS X 10.6.4 the code compiles and runs without error.

uname:

Linux merlin 2.6.18-194.11.4.el5 #1 SMP Fri Sep 17 04:55:26 EDT 2010 i686 i686 i386 GNU/Linux

* (lisp-implementation-version)

"1.0.38"

And:

* array-total-size-limit

536870909

-pete

Ariel Badichi

unread,
Nov 10, 2010, 5:54:49 PM11/10/10
to
Peter Keller <psi...@cs.wisc.edu> writes:

It looks like both you and Andy Venikov are using 32-bit SBCL. Here's
what I get on a 64-bit SBCL:

CL-USER> array-total-size-limit
1152921504606846973
CL-USER> most-positive-fixnum
1152921504606846975

> -pete

Ariel

Thomas A. Russ

unread,
Nov 10, 2010, 5:41:13 PM11/10/10
to
Peter Keller <psi...@cs.wisc.edu> writes:

> 1000000000 is a fixnum? Seems kinda large.... I think j won't be the thing
> you think it is.

It is on a 64-bit lisp implementation. ;-)

You are correct that it is likely not to be on a 32-bit implementation.
Often you only get 29 bits there. Or sometimes less like 24 or even
(gasp!) 16....

Barry Margolin

unread,
Nov 10, 2010, 8:26:46 PM11/10/10
to
In article <87fwv8e...@kuiper.lan.informatimago.com>,

p...@informatimago.com (Pascal J. Bourguignon) wrote:

> Zach Beane <xa...@xach.com> writes:
>
> > Peter Keller <psi...@cs.wisc.edu> writes:
> >
> >> 1000000000 is a fixnum? Seems kinda large.... I think j won't be the thing
> >> you think it is.
> >
> > 30 bits not a fixnum? Here's a nickel, kid, get yourself an AMD64!
>
> Actually, I'd be more concerned about ARRAY-TOTAL-SIZE-LIMIT. An
> implementation very speed conscious could set it to 1024 to be able to
> keep arrays inside a few L1-data cache lines.

Although the CL standard allows such a low limit, any implementation
that does this is clearly just a toy, not meant for serious use. Maybe
it might be for something like a special microcontroller, or if you were
going retro and implementing Lisp for the PDP-8. In any case, I don't
think anyone needs to be worried about this in the real world.

--
Barry Margolin, bar...@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
*** PLEASE don't copy me on replies, I'll read them in the group ***

Peter Keller

unread,
Nov 10, 2010, 8:34:45 PM11/10/10
to
Ariel Badichi <vermil...@gmail.com> wrote:
> It looks like both you and Andy Venikov are using 32-bit SBCL. Here's
> what I get on a 64-bit SBCL:
>
> CL-USER> array-total-size-limit
> 1152921504606846973
> CL-USER> most-positive-fixnum
> 1152921504606846975

Where that nickel so I can get myuself a 64-bit machine? I think I
dropped it...

-pete

TheFlyingDutchman

unread,
Nov 11, 2010, 3:34:21 AM11/11/10
to

>
> 30 bits not a fixnum? Here's a nickel, kid, get yourself an AMD64!
>
I've got an AMD64 system running Windows 7 64-bit OS. And as it turns
out, three 32 bit implementations of Common Lisp. It is interesting
the difference in error messages I got Running the primes function.

Free Edition of Allegro CL 8.2:

Compile:
Warning: compiler-macro for MAKE-ARRAY signalled SIMPLE-ERROR:
1000000001 is an illegal dimension list
Warning: The form 1000000000 evaluated to 1000000000, which was not of
the anticipated type FIXNUM.
Current LOOP context: FOR J FIXNUM FROM (THE FIXNUM (* I I)) TO
1000000000 BY I DO.

Run
Error: 1000000001 is an illegal dimension list
[condition type: SIMPLE-ERROR]
-----------------------------------------------------------------
Lispworks Personal Edition 5.1.1

Run
Bad arguments to SYSTEM::MAKE-BIT-VECTOR: 1000000001 0 .
-----------------------------------------------------------------
SBCL 1.0.37

Compile


; in: LAMBDA NIL
; (LOOP FOR J FIXNUM FROM (THE FIXNUM (* I I)) TO 1000000000 BY I
; DO (SETF (SBIT MEMO J) 1))
;
; caught WARNING:
; (in macroexpansion of (LOOP FOR J ...))
; (hint: For more precise location, try *BREAK-ON-SIGNALS*.)
; The form 1000000000 evaluated to 1000000000, which was not of the
anticipated
; type (AND FIXNUM REAL).
; current LOOP context: FOR J FIXNUM FROM (THE FIXNUM
; (* I I)) TO
1000000000 BY I DO.

; (MAKE-ARRAY 1000000001 :ELEMENT-TYPE 'BIT :INITIAL-ELEMENT 0)


;
; caught WARNING:
; Asserted type (OR (MOD 536870909) CONS NULL) conflicts with
derived type
; (VALUES (INTEGER 1000000001 1000000001) &OPTIONAL).
; See also:
; The SBCL Manual, Node "Handling of Types"
;
; compilation unit finished

; caught 2 WARNING conditions

Run
unhandled TYPE-ERROR:


The value 1000000001 is not of type (OR (MOD 536870909) CONS NULL).

_________________________________________________________________________________

Type "(OR (MOD 536870909) CONS NULL)" ?????

Piotr Chamera

unread,
Nov 11, 2010, 3:50:38 AM11/11/10
to
W dniu 2010-11-11 09:34, TheFlyingDutchman pisze:

> unhandled TYPE-ERROR:
> The value 1000000001 is not of type (OR (MOD 536870909) CONS NULL).
> _________________________________________________________________________________
>
> Type "(OR (MOD 536870909) CONS NULL)" ?????

Expected type of array dimension argument is

integer in range 0 - 536870908

or

list [of dimensions]

or

nil

TheFlyingDutchman

unread,
Nov 11, 2010, 12:52:36 PM11/11/10
to

> > Type "(OR (MOD 536870909) CONS NULL)"   ?????
>
> Expected type of array dimension argument is
>
> integer in range 0 - 536870908
>
> or
>
> list [of dimensions]
>
> or
>
> nil

Thanks for the explanation! SBCL's is a bit too terse, too obtuse and
too brainiac for me. How about
"Type not one of (fixnum, list, nil)" ?

But SBCL does do this as well:
(type-of 2) => (INTEGER 0 536870911)
(type-of -2) => FIXNUM

I also don't understand why SBCL outputs the compiler messages as
comments.

TheFlyingDutchman

unread,
Nov 11, 2010, 1:50:06 PM11/11/10
to
Type "(OR (MOD 536870909) CONS NULL)"

Since - (type-of 2) => (INTEGER 0 536870911) - and as NULL is
represented in the language as nil, I would expect the above to be
Type "(OR ((INTEGER 0 536870911) CONS nil)

The CONS part is a problematic issue of the language as (ironically)
there is no abstract list type to replace it with.

(make-array CONS)
(make-array '(1 . 2)) => "debugger invoked on a TYPE-ERROR: The value
2 is not of type LIST."

Note that the second error message said "LIST" while the first said
"CONS" and yet they were talking about the same thing!?!?

Zach Beane

unread,
Nov 11, 2010, 1:52:12 PM11/11/10
to
TheFlyingDutchman <zzbb...@aol.com> writes:

> Type "(OR (MOD 536870909) CONS NULL)"
>
> Since - (type-of 2) => (INTEGER 0 536870911) - and as NULL is
> represented in the language as nil, I would expect the above to be
> Type "(OR ((INTEGER 0 536870911) CONS nil)

NIL-the-object is quite different from NIL-the-type. NULL is an accurate
type for the object NIL. No object is of type NIL.

Zach

TheFlyingDutchman

unread,
Nov 11, 2010, 8:46:49 PM11/11/10
to
On Nov 11, 10:52 am, Zach Beane <x...@xach.com> wrote:
Yeah, thanks, I have run across this concept via the null function and
scratched my head some. (nil x) seemed more appropriate than (null x).
So does a CL if statement check for object eq nil or object with type
of null? If object eq


Anyway, it's clear that nil is nil and nil is null, but null isn't
nil. Showing that nil is more flexible than null. ;)

Pascal J. Bourguignon

unread,
Nov 12, 2010, 7:11:26 AM11/12/10
to
TheFlyingDutchman <zzbb...@aol.com> writes:

No, the first one didn't say CONS, it said (OR CONS NULL) = LIST.

Pascal J. Bourguignon

unread,
Nov 12, 2010, 7:16:31 AM11/12/10
to
TheFlyingDutchman <zzbb...@aol.com> writes:

No. NIL is not NULL. NIL *IS-A* NULL.

(eq 'nil 'null) --> nil
(typep 'nil 'null) --> t

You seem to have a hard time with some quite basic concepts...

Tim Bradshaw

unread,
Nov 12, 2010, 7:59:25 AM11/12/10
to
On 2010-11-12 12:16:31 +0000, Pascal J. Bourguignon said:

> No. NIL is not NULL. NIL *IS-A* NULL.

And no objects are of type NIL (it is the bottom of the type graph),
while all objects are of type T (the top of the type graph). (You know
this, I know: the OP may not).

Tamas K Papp

unread,
Nov 12, 2010, 9:32:20 AM11/12/10
to
On Fri, 12 Nov 2010 12:59:25 +0000, Tim Bradshaw wrote:

> On 2010-11-12 12:16:31 +0000, Pascal J. Bourguignon said:
>
>> No. NIL is not NULL. NIL *IS-A* NULL.
>
> And no objects are of type NIL (it is the bottom of the type graph),

I learned this after getting a neat error message in SBCL after typing
(make-array nil :element-type nil) in the REPL.

Clearly, the designers of ANSI CL really thought about all the corner
cases.

Tamas

Zach Beane

unread,
Nov 12, 2010, 9:53:21 AM11/12/10
to

Oh? Should (stringp (make-array 0 :element-type nil)) be T or NIL? Why
or why not? Do implementations agree with you, and with each other?

Zach

Pascal J. Bourguignon

unread,
Nov 12, 2010, 10:15:27 AM11/12/10
to
Zach Beane <xa...@xach.com> writes:

Indeed. :-)


[pjb@kuiper localhost:10.0 ~]$ clall '(stringp (make-array 0 :element-type nil))'

========================================================================
Implementation: Armed Bear Common Lisp 0.20.0 on I386

Reading of: "(stringp (make-array 0 :element-type nil))"
signaled no error

Evaluation of: (STRINGP (MAKE-ARRAY 0 :ELEMENT-TYPE NIL))
signaled no error
wrote nothing on *ERROR-OUTPUT*
wrote nothing on *STANDARD-OUTPUT*
returned the following value:
--> T

========================================================================
Implementation: International Allegro CL Free Express Edition 8.2 [Linux (x86)] (Sep 11, 2010 7:36) on x86

Reading of: "(stringp (make-array 0 :element-type nil))"
signaled no error

Evaluation of: (STRINGP (MAKE-ARRAY 0 :ELEMENT-TYPE NIL))
signaled no error
wrote nothing on *ERROR-OUTPUT*
wrote nothing on *STANDARD-OUTPUT*
returned the following value:
--> NIL

========================================================================
Implementation: Clozure Common Lisp Version 1.5 (LinuxX8664) on x86_64

Reading of: "(stringp (make-array 0 :element-type nil))"
signaled no error

Evaluation of: (STRINGP (MAKE-ARRAY 0 :ELEMENT-TYPE NIL))
signaled no error
wrote nothing on *ERROR-OUTPUT*
wrote nothing on *STANDARD-OUTPUT*
returned the following value:
--> NIL

========================================================================
Implementation: CLISP 2.49 (2010-07-07) (built 3498306068) (memory 3498306421) on X86_64

Reading of: "(stringp (make-array 0 :element-type nil))"
signaled no error

Evaluation of: (STRINGP (MAKE-ARRAY 0 :ELEMENT-TYPE NIL))
signaled no error
wrote nothing on *ERROR-OUTPUT*
wrote nothing on *STANDARD-OUTPUT*
returned the following value:
--> T

========================================================================
Implementation: CMU Common Lisp 19c (19C) on X86

Reading of: "(stringp (make-array 0 :element-type nil))"
signaled no error

Evaluation of: (STRINGP (MAKE-ARRAY 0 :ELEMENT-TYPE NIL))
signaled no error
wrote nothing on *ERROR-OUTPUT*
wrote nothing on *STANDARD-OUTPUT*
returned the following value:
--> T

========================================================================
Implementation: ECL 10.7.1 on x86_64

Reading of: "(stringp (make-array 0 :element-type nil))"
signaled no error

Evaluation of: (STRINGP (MAKE-ARRAY 0 :ELEMENT-TYPE NIL))
signaled the following error:
#<a SIMPLE-ERROR>
ECL does not support arrays with element type NIL
wrote nothing on *ERROR-OUTPUT*
wrote nothing on *STANDARD-OUTPUT*
returned no value
========================================================================
Implementation: SBCL 1.0.19-gentoo on X86-64

Reading of: "(stringp (make-array 0 :element-type nil))"
signaled no error

Evaluation of: (STRINGP (MAKE-ARRAY 0 :ELEMENT-TYPE NIL))
signaled no error
wrote nothing on *ERROR-OUTPUT*
wrote nothing on *STANDARD-OUTPUT*
returned the following value:
--> T


========================================================================

TheFlyingDutchman

unread,
Nov 12, 2010, 10:22:43 AM11/12/10
to
On Nov 12, 4:16 am, p...@informatimago.com (Pascal J. Bourguignon)
wrote:

> TheFlyingDutchman <zzbba...@aol.com> writes:
> > On Nov 11, 10:52 am, Zach Beane <x...@xach.com> wrote:
> >> TheFlyingDutchman <zzbba...@aol.com> writes:
> >> > Type "(OR (MOD 536870909) CONS NULL)"
>
> >> > Since - (type-of 2) => (INTEGER 0 536870911) - and as NULL is
> >> > represented in the language as nil, I would expect the above to be
> >> > Type "(OR ((INTEGER 0 536870911) CONS nil)
>
> >> NIL-the-object is quite different from NIL-the-type. NULL is an accurate
> >> type for the object NIL. No object is of type NIL.
>
> > Yeah, thanks, I have run across this concept via the null function and
> > scratched my head some. (nil x) seemed more appropriate than (null x).
> > So does a CL if statement check for object eq nil or object with type
> > of null? If object eq
>
> > Anyway, it's clear that nil is nil and nil is null, but null isn't
> > nil. Showing that nil is more flexible than null.  ;)
>
> No.  NIL is not NULL.  NIL  *IS-A*   NULL.
>
> (eq     'nil  'null)  --> nil
> (typep  'nil  'null)  --> t
>
> You seem to have a hard time with some quite basic concepts...
>
You seemed to have missed the fact that I said "Showing that nil is
more flexible than null. ;)".

^^^^^^

TheFlyingDutchman

unread,
Nov 12, 2010, 11:02:12 AM11/12/10
to
On Nov 12, 4:11 am, p...@informatimago.com (Pascal J. Bourguignon)
wrote:

> TheFlyingDutchman <zzbba...@aol.com> writes:
> > Type "(OR (MOD 536870909) CONS NULL)"
>
> > Since  - (type-of 2) => (INTEGER 0 536870911) - and as NULL is
> > represented in the language as nil,  I would expect the above to be
> > Type "(OR ((INTEGER 0 536870911) CONS nil)
>
> > The CONS part is a problematic issue of the language as (ironically)
> > there is no abstract list type to replace it with.
>
> > (make-array CONS)
> > (make-array '(1 . 2)) => "debugger invoked on a TYPE-ERROR: The value
> > 2 is not of type LIST."
>
> > Note that the second error message said "LIST" while the first said
> > "CONS" and yet they were talking about the same thing!?!?
>
> No, the first one didn't say CONS, it said (OR CONS NULL) = LIST.
>
(OR CONS NULL) = LIST (type-of '(1 . 2)) => CONS (OR '(1 . 2)
NULL) = LIST

(OR LIST VECTOR) = SEQUENCE (OR (OR CONS NULL) VECTOR) = SEQUENCE
(OR (OR '(1 . 2) NULL) VECTOR) = SEQUENCE

(ELT ..... LENGTH ..... MISMATCH) = Functions that work on a
SEQUENCE.

(ELT .... LENGTH .... MISMATCH) = Functions that work on (OR LIST
VECTOR)

(ELT .... LENGTH .... MISMATCH) = Functions that work on (OR (OR CONS
NULL) VECTOR)

(ELT .... LENGTH .... MISMATCH) = Functions that work on (OR (OR
'(1 . 2) NULL) VECTOR)

(length '(1 . 2)) => "debugger invoked on a TYPE-ERROR: The value 2 is

Tim Bradshaw

unread,
Nov 12, 2010, 11:18:28 AM11/12/10
to
On 2010-11-12 14:53:21 +0000, Zach Beane said:

> Oh? Should (stringp (make-array 0 :element-type nil)) be T or NIL? Why
> or why not? Do implementations agree with you, and with each other?

It may be T or NIL (and I bet you know this :-).

When creating an array an implementation may return an array
specialised to any supertype of the requested element type. So,
since all types are supertypes of NIL, the resulting array may have any
element type at all, and this includes strings.

If the implementation does support arrays of element type NIL (which
seems a bit unlikely since they are not generally very useful as far as
I can see), then the answer is NIL: NIL is a subtype of every type, and
a STRING is an array whose elements are of type CHARACTER or a subtype
of type CHARACTER, which this is.

--tim

Tim Bradshaw

unread,
Nov 12, 2010, 11:20:52 AM11/12/10
to
On 2010-11-12 16:18:28 +0000, Tim Bradshaw said:
>
>
> If the implementation does support arrays of element type NIL (which
> seems a bit unlikely since they are not generally very useful as far as
> I can see), then the answer is NIL: NIL is a subtype of every type, and
> a STRING is an array whose elements are of type CHARACTER or a subtype
> of type CHARACTER, which this is.

I meant the answer is T (an array whose actual element type is NIL is a
string).

Tamas K Papp

unread,
Nov 12, 2010, 11:23:00 AM11/12/10
to
On Fri, 12 Nov 2010 16:15:27 +0100, Pascal J. Bourguignon wrote:

> Zach Beane <xa...@xach.com> writes:
>
>> Tamas K Papp <tkp...@gmail.com> writes:
>>
>>> On Fri, 12 Nov 2010 12:59:25 +0000, Tim Bradshaw wrote:
>>>
>>>> On 2010-11-12 12:16:31 +0000, Pascal J. Bourguignon said:
>>>>
>>>>> No. NIL is not NULL. NIL *IS-A* NULL.
>>>>
>>>> And no objects are of type NIL (it is the bottom of the type graph),
>>>
>>> I learned this after getting a neat error message in SBCL after typing
>>> (make-array nil :element-type nil) in the REPL.
>>>
>>> Clearly, the designers of ANSI CL really thought about all the corner
>>> cases.
>>
>> Oh? Should (stringp (make-array 0 :element-type nil)) be T or NIL? Why
>> or why not? Do implementations agree with you, and with each other?

I don't really see why implementations should agree on this.
:element-type justs requests an array element type, and it may be
upgraded if the implementation doesn't support that specialized type.
NIL is not in 15.1.2.2, and since nil is a subtype of every type [1],
the implementation is free to return an array of any element type.

Depending on whether this upgraded array element type is a subtype of
char or not, stringp can return nil or t.

Is this interpretation incorrect?

I don't see how the implementations could agree on the result of
(stringp (make-array 0 :element-type nil)) without either requiring
NIL in 15.1.2.2, or making it a special case for make-array.

[1] http://www.lispworks.com/documentation/HyperSpec/Body/t_nil.htm

> [pjb@kuiper localhost:10.0 ~]$ clall '(stringp (make-array 0
> :element-type nil))'

Where can I get clall? (Google kindly gives me the Crystal Lake
American Little League website).

Best,

Tamas

Duane Rettig

unread,
Nov 12, 2010, 11:44:56 AM11/12/10
to

I agree with you in principle; the designers of the Ansi Common Lisp
spec did think things through very carefully in many areas that are
important in day-to-day programming. But they are, after all, not
perfect, and so there are some issues with the spec, including the one
Zach pointed out, many of which are listed here:

http://www.cliki.net/Proposed%20ANSI%20Revisions%20and%20Clarifications

Some represent clear omissions and mistakes, some even typographical,
and others represent head-scratchers, like the array-of-type-nil-is-a-
string question.

All that aside, the only interesting thing about an array of element-
type nil is the fact that it is not interesting.

Duane

Tim Bradshaw

unread,
Nov 12, 2010, 11:47:38 AM11/12/10
to
On 2010-11-12 16:44:56 +0000, Duane Rettig said:

> But they are, after all, not
> perfect, and so there are some issues with the spec, including the one
> Zach pointed out,

I don't think that's an issue with the spec is it (if you mean the
(make-array 0 :element-type nil) one)?

TheFlyingDutchman

unread,
Nov 12, 2010, 12:44:00 PM11/12/10
to

>
> And no objects are of type NIL (it is the bottom of the type graph),
> while all objects are of type T (the top of the type graph).  (You know
> this, I know: the OP may not).

Does a CL if statement check for object eq nil or object with type
of null?

Tim Bradshaw

unread,
Nov 12, 2010, 12:47:30 PM11/12/10
to
On 2010-11-12 17:44:00 +0000, TheFlyingDutchman said:
>
> Does a CL if statement check for object eq nil or object with type
> of null?

There is no possible difference: NIL is the unique object whose type is NULL.

Pascal J. Bourguignon

unread,
Nov 12, 2010, 2:02:23 PM11/12/10
to
TheFlyingDutchman <zzbb...@aol.com> writes:

> On Nov 12, 4:11�am, p...@informatimago.com (Pascal J. Bourguignon)
> wrote:
>> TheFlyingDutchman <zzbba...@aol.com> writes:
>> > Type "(OR (MOD 536870909) CONS NULL)"
>>
>> > Since �- (type-of 2) => (INTEGER 0 536870911) - and as NULL is
>> > represented in the language as nil, �I would expect the above to be
>> > Type "(OR ((INTEGER 0 536870911) CONS nil)
>>
>> > The CONS part is a problematic issue of the language as (ironically)
>> > there is no abstract list type to replace it with.
>>
>> > (make-array CONS)
>> > (make-array '(1 . 2)) => "debugger invoked on a TYPE-ERROR: The value
>> > 2 is not of type LIST."
>>
>> > Note that the second error message said "LIST" while the first said
>> > "CONS" and yet they were talking about the same thing!?!?
>>
>> No, the first one didn't say CONS, it said (OR CONS NULL) = LIST.
>>
> (OR CONS NULL) = LIST (type-of '(1 . 2)) => CONS
> (OR '(1 . 2) NULL) = LIST

QUOTE is not a valid type specifier.


> (OR LIST VECTOR) = SEQUENCE (OR (OR CONS NULL) VECTOR) = SEQUENCE
> (OR (OR '(1 . 2) NULL) VECTOR) = SEQUENCE

You mean (typep '(1 . 2) 'sequence)
This is exact.


> (ELT ..... LENGTH ..... MISMATCH) = Functions that work on a
> SEQUENCE.

This is wrong. Alsmost all the function that work on sequences, don't
work on the type SEQUENCE, but on the subset of that type that contains
only the proper lists. This is explicitely specified. Why do I have to
repeat it here?

See: "17.1.1 General Restrictions on Parameters that must be Sequences"


> (length '(1 . 2)) => "debugger invoked on a TYPE-ERROR: The value 2 is
> not of type LIST".

Indeed. And even more strangely:

CL-USER> (mapcar 'print '(1 . 2))

1
*** - MAPCAR: A proper list must not end with 2

because, not only functions working on sequence don't work on the
SEQUENCE type, but also functions working on lists, don't work on the
LIST type.

See: "14.1.2.3 General Restrictions on Parameters that must be Lists"


Notice however that an implementation may produce this result (and most
do):

[pjb@kuiper localhost:10.0 ~]$ clall -r '(mapcar (function list) (quote (un)) (quote (1 . 2)) (quote #1=(one . #1#)))'

Armed Bear Common Lisp The value 2 is not of type LIST.
International Allegro CL Free Express Edition --> ((UN 1 ONE))
Clozure Common Lisp --> ((UN 1 ONE))
CLISP --> ((UN 1 ONE))
CMU Common Lisp --> ((UN 1 ONE))
ECL --> ((UN 1 ONE))
SBCL --> ((UN 1 ONE))

========================================================================

On the other hand, if you put the improper list first:


[pjb@kuiper localhost:10.0 ~]$ clall -r '(mapcar (function list) (quote (1 . 2)) (quote #1=(one . #1#)) (quote (un)))'

Armed Bear Common Lisp The value 2 is not of type LIST.
International Allegro CL Free Express Edition --> ((1 ONE UN))
Clozure Common Lisp --> ((1 ONE UN))
CLISP MAPCAR: A proper list must not end with 2
CMU Common Lisp --> ((1 ONE UN))
ECL In function MAPCAR, the value of the second argument is 2 which is not of the expected type LIST
SBCL --> ((1 ONE UN))

========================================================================

Pascal J. Bourguignon

unread,
Nov 12, 2010, 2:08:21 PM11/12/10
to

Pascal J. Bourguignon

unread,
Nov 12, 2010, 2:14:36 PM11/12/10
to
Duane Rettig <du...@franz.com> writes:

I don't agree. It is quite interesting to make such objects. It is
under-used.

Everywhere when a gensym is used but a symbol is not needed, or where a
cons cell is created, but nothing to be stored there, a nil array would
be a better way to get an empty object with identity.

(loop with eof = (make-array 0 :element-type nil)
for d = (read stream nil eof)
until (eql d eof)
do ...)

Tim Bradshaw

unread,
Nov 12, 2010, 3:01:03 PM11/12/10
to
On 2010-11-12 19:14:36 +0000, Pascal J. Bourguignon said:

> Everywhere when a gensym is used but a symbol is not needed, or where a
> cons cell is created, but nothing to be stored there, a nil array would
> be a better way to get an empty object with identity.
>
> (loop with eof = (make-array 0 :element-type nil)
> for d = (read stream nil eof)
> until (eql d eof)
> do ...)

It's not the requested element-type being nil that makes it interesting
though, it's the dimensions being 0. Indeed many implementations will
probably give you an array whose element-type is T.

TheFlyingDutchman

unread,
Nov 12, 2010, 4:48:03 PM11/12/10
to

> >> No, the first one didn't say CONS, it said (OR CONS NULL) = LIST.
>
> > (OR CONS NULL) = LIST     (type-of '(1 . 2)) => CONS
> > (OR '(1 . 2) NULL) = LIST
>
> QUOTE is not a valid type specifier.

QUOTE is not being used to specify a type, but to specify an object of
a type which is substituted for the type in the equation. '(1 . 2) is
an object of type CONS. I know if I have an object of type CONS or an
object of type NULL, I have a list because we have to have a list if
we have either an object of type NULL or an object of type CONS per:


(OR CONS NULL) = LIST

Object nil is a list (type NULL), object '(1 2 3) is a list (type
CONS), and object '(1 . 2) is a list (type CONS).

>
> > (OR LIST VECTOR) = SEQUENCE  (OR (OR CONS NULL) VECTOR) = SEQUENCE
> > (OR (OR '(1 . 2) NULL) VECTOR) = SEQUENCE
>
> You mean (typep '(1 . 2) 'sequence)
> This is exact.
>
> > (ELT .....  LENGTH ..... MISMATCH) = Functions that work on a
> > SEQUENCE.
>
> This is wrong.  Alsmost all the function that work on sequences, don't
> work on the type SEQUENCE, but on the subset of that type that contains
> only the proper lists.

What is the name of the Common Lisp type or Common Lisp types that
sequence functions work on?

> This is explicitely specified. Why do I have to repeat it here?

Well it's not a repeat for me because I was studying "Chapter 14 -
Sequences" of a book called "Common Lisp the Language" (2nd edition)
by Guy L. Steele Jr. It does not mention proper lists, circular
lists, dotted pairs or dotted lists. Just lists and vectors.

Pascal J. Bourguignon

unread,
Nov 12, 2010, 5:07:40 PM11/12/10
to
TheFlyingDutchman <zzbb...@aol.com> writes:

>> >> No, the first one didn't say CONS, it said (OR CONS NULL) = LIST.
>>
>> > (OR CONS NULL) = LIST � � (type-of '(1 . 2)) => CONS
>> > (OR '(1 . 2) NULL) = LIST
>>
>> QUOTE is not a valid type specifier.
>
> QUOTE is not being used to specify a type, but to specify an object of
> a type which is substituted for the type in the equation. '(1 . 2) is
> an object of type CONS. I know if I have an object of type CONS or an
> object of type NULL, I have a list because we have to have a list if
> we have either an object of type NULL or an object of type CONS per:
> (OR CONS NULL) = LIST
>
> Object nil is a list (type NULL), object '(1 2 3) is a list (type
> CONS), and object '(1 . 2) is a list (type CONS).
>
>>
>> > (OR LIST VECTOR) = SEQUENCE �(OR (OR CONS NULL) VECTOR) = SEQUENCE
>> > (OR (OR '(1 . 2) NULL) VECTOR) = SEQUENCE
>>
>> You mean (typep '(1 . 2) 'sequence)
>> This is exact.
>>
>> > (ELT ..... �LENGTH ..... MISMATCH) = Functions that work on a
>> > SEQUENCE.
>>
>> This is wrong. �Alsmost all the function that work on sequences, don't
>> work on the type SEQUENCE, but on the subset of that type that contains
>> only the proper lists.
>
> What is the name of the Common Lisp type or Common Lisp types that
> sequence functions work on?

It has no name. You could define that type in CL, modulo the fact that
implementations are allowed to add subtypes to SEQUENCE on which these
function will work.

(defun proper-list-p (object)
"
RETURN: whether object is a proper list
NOTE: terminates with any kind of list, dotted, circular, etc.
"
(labels ((proper (current slow)
(cond ((null current) t)
((atom current) nil)
((null (cdr current)) t)
((atom (cdr current)) nil)
((eq current slow) nil)
(t (proper (cddr current) (cdr slow))))))
(and (listp object) (proper object (cons nil object)))))


(deftype proper-list () '(satisfies proper-list-p))

(deftype pl-or-vector () '(or proper-list vector))

Any object of that type will be accepted by CL sequence functions (plus
some more, implementation defined).


>> This is explicitely specified. Why do I have to repeat it here?
>
> Well it's not a repeat for me because I was studying "Chapter 14 -
> Sequences" of a book called "Common Lisp the Language" (2nd edition)

The reference is CLHS.


> by Guy L. Steele Jr. It does not mention proper lists, circular
> lists, dotted pairs or dotted lists. Just lists and vectors.

See page 32.

It is usually assumed that when you read page 419, you've read all the
previous pages.

Otherwise, indeed, it's strange that in CLtL2, the List chapter is after
the Sequence chapter.

TheFlyingDutchman

unread,
Nov 12, 2010, 6:47:42 PM11/12/10
to

>
> It has no name.  You could define that type in CL, modulo the fact that
> implementations are allowed to add subtypes to SEQUENCE on which these
> function will work.
>
> (defun proper-list-p (object)
>   "
> RETURN: whether object is a proper list
> NOTE:   terminates with any kind of list, dotted, circular, etc.
> "
>   (labels ((proper (current slow)
>              (cond ((null current)       t)
>                    ((atom current)       nil)
>                    ((null (cdr current)) t)
>                    ((atom (cdr current)) nil)
>                    ((eq current slow)    nil)
>                    (t                    (proper (cddr current) (cdr slow))))))
>     (and (listp object) (proper object (cons nil object)))))
>
> (deftype proper-list () '(satisfies proper-list-p))
>
> (deftype pl-or-vector () '(or proper-list vector))
>
> Any object of that type will be accepted by CL sequence functions (plus
> some more, implementation defined).

Thanks! That should be a part of the next version of the standard...

>
> >> This is explicitely specified. Why do I have to repeat it here?
>
> > Well it's not a repeat for me because I was studying  "Chapter 14 -
> > Sequences" of a book called "Common Lisp the Language" (2nd edition)
>
> The reference is CLHS.
>
> > by Guy L. Steele Jr.  It does not mention proper lists, circular
> > lists, dotted pairs or dotted lists. Just lists and vectors.
>
> See page 32.

I see Guy Steele does not use the term "proper list". He uses "true
list".

He says on page 33: "Most functions advertised to operate on lists
expect to
be given true lists. Throughout this book, unless otherwise specified,
it is an
error to pass a dotted list to a function that is specified to require
a list as an
argument."

That would have been a great point to state why he wasn't going to use
the term "true list" throughout instead of list.

> It is usually assumed that when you read page 419, you've read all the
> previous pages.

That's a dangerous assumption for a programming text. Some of use are
cargo cult programmers and we don't always read sequentially or even
cover to cover.


Thomas A. Russ

unread,
Nov 12, 2010, 8:28:31 PM11/12/10
to
TheFlyingDutchman <zzbb...@aol.com> writes:

> >
> > It has no name. □You could define that type in CL, modulo the fact that
> > implementations are allowed to add subtypes to SEQUENCE on which these
> > function will work.

[Snip definition of proper-list-p]

> > Any object of that type will be accepted by CL sequence functions (plus
> > some more, implementation defined).
>
> Thanks! That should be a part of the next version of the standard...

I doubt it. I'm certain it was very specifically not included in the
standard as one of the types.in the type system. It is defined in the
glossary of terms, so it was clearly known to the specification
writers. But an examination of Pascal's test function shows that
determining if a list is a proper list is not easy and I imagine that
for performance reasons, they committee did not want to include it as
one of the standard types. Otherwise people would end up doing things
like
(typecase (proper-list ...)
...)
which would require making an expensive type test rather than all of the
other ones that are really fast. The other built-in types are all easily
determined.

> I see Guy Steele does not use the term "proper list". He uses "true
> list".
>
> He says on page 33: "Most functions advertised to operate on lists
> expect to be given true lists. Throughout this book, unless otherwise
> specified, it is an error to pass a dotted list to a function that is
> specified to require a list as an argument."
>
> That would have been a great point to state why he wasn't going to use
> the term "true list" throughout instead of list.

Because most of the time when talking about lists in Lisp, we mean true
or proper lists. "Proper list" is the official specification term.

It would be rather verbose otherwise. Instead, the less common circular
lists and dotted pairs are called out when they are needed.

Circular lists will often give fits to the sequence functions as well.
OK, I guess techincally you won't get an error from using a circular
list, but you still often won't like the results. Although I can think
of some circumstances where they can be used. Consider

(loop with year = 2010 and month = 'november
for day from 1 to 30
as dow in '#1=(monday tuesday wednesday thursday friday saturday
sunday . #1#)
collect (list dow month day year))


> > It is usually assumed that when you read page 419, you've read all the
> > previous pages.
>
> That's a dangerous assumption for a programming text. Some of use are
> cargo cult programmers and we don't always read sequentially or even
> cover to cover.

Well, you would never complete a book if every mention of a term later
in the book had to repeat the definition from earlier in the book. I
think that would lead to infinite recursion. ;-)

--
Thomas A. Russ, USC/Information Sciences Institute

Pascal J. Bourguignon

unread,
Nov 12, 2010, 10:00:24 PM11/12/10
to
TheFlyingDutchman <zzbb...@aol.com> writes:

Not on my edition. Are you reading 2nd edition?

2.4. Lists and Conses

A LIST is recursively defined to be either the empty list or a
cons whose CDR component is a list. A list is therefore a
chain of conses linked by their cdr components and terminated
by nil, the empty list.

This is the definition of proper lists. Therefore in this book, when he
talks of lists, he actually talks of proper lists.

In the same section:

A DOTTED LIST is one whose last cons does not have nil for its
cdr, rather some other data object (which is also not a cons, or
the first mentionned cons would not be the last cons of the
list).

He doesn't define circular lists, but it doesn't matter, since he
defined LISTs to be proper lists.

> He says on page 33: "Most functions advertised to operate on lists
> expect to be given true lists. Throughout this book, unless otherwise
> specified, it is an error to pass a dotted list to a function that is
> specified to require a list as an argument."

I don't see that in my copy. But in this case, you don't have an
excuse.

> That would have been a great point to state why he wasn't going to use
> the term "true list" throughout instead of list.

I guess you can find bugs in books as well as in programs.


>> It is usually assumed that when you read page 419, you've read all the
>> previous pages.
>

> That's a dangerous assumption for a programming text. Some of us are


> cargo cult programmers and we don't always read sequentially or even
> cover to cover.

We don't expect that of lispers.

TheFlyingDutchman

unread,
Nov 13, 2010, 12:50:11 AM11/13/10
to
On Nov 12, 7:00 pm, p...@informatimago.com (Pascal J. Bourguignon)
wrote:

I was reading it online in HTML format and they talk about true list
here:

http://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node28.html#SECTION00640000000000000000

but since you referenced a page number I found a PDF copy with it on
numbered page 33 here:

http://www.lispmachine.net/books/common_lisp_the_language.pdf

>
>     2.4. Lists and Conses
>
>          A LIST is recursively defined to be either the empty list or a
>          cons whose CDR component is a list.  A list is therefore a
>          chain of conses linked by their cdr components and terminated
>          by nil, the empty list.
>
> This is the definition of proper lists.  Therefore in this book, when he
> talks of lists, he actually talks of proper lists.
>
> In the same section:
>
>         A DOTTED LIST is one whose last cons does not have nil for its
>         cdr, rather some other data object (which is also not a cons, or
>         the first mentionned cons would not be the last cons of the
>         list).
>
> He doesn't define circular lists, but it doesn't matter, since he
> defined LISTs to be proper lists.

On the NEXT page is where he talks about "true lists" and lists.

'When the distinction is important, the term "true list" will be used
to refer to a list terminated by nil.'

Per the well established rules of sequential cover to cover book
reading page precedence, his later statements are in effect for the
balance of the book, if and until superceded. So from page 33 on a
list should be taken to be a superset of a proper list.


> > That's a dangerous assumption for a programming text. Some of us are
> > cargo cult programmers and we don't always read sequentially or even
> > cover to cover.
>
> We don't expect that of lispers.

It might be time to broaden the base. There can be strength in
numbers. If the Lisp community reaches out and embraces cargo cult
programmers, programmers who have spent time at community colleges,
programmers who have graduated from schools with State in their name,
programmers who learn from paperback books that have a completion time
in their title, programmers who drink beer from a bottle - they may
find such momentum that issues like listp not passing its' test suite
and '(1 . 2) having the same type as '(1 2) is resolved in a
subsequent version of the language. Imagine an International List
Conference that is held at San Francisco's Moscone Convention Center -
north AND south buildings and requires more staff to support it than
JavaOne.

TheFlyingDutchman

unread,
Nov 13, 2010, 2:20:02 AM11/13/10
to

>
> I doubt it.  I'm certain it was very specifically not included in the
> standard as one of the types.in the type system.  It is defined in the
> glossary of terms, so it was clearly known to the specification
> writers.  But an examination of Pascal's test function shows that
> determining if a list is a proper list is not easy and I imagine that
> for performance reasons, they committee did not want to include it as
> one of the standard types.  Otherwise people would end up doing things
> like
>   (typecase (proper-list ...)
>      ...)
> which would require making an expensive type test rather than all of the
> other ones that are really fast.  The other built-in types are all easily
> determined.

It would be nice if the next standard did it internally in an
efficient manner. An abstract list type or new type LIST-NODE that has
new functions like add-to-list that do not allow the creation of LIST-
NODEs that are dotted or circular.


 
>
> Circular lists will often give fits to the sequence functions as well.
> OK, I guess techincally you won't get an error from using a circular
> list, but you still often won't like the results.  Although I can think
> of some circumstances where they can be used.  Consider
>
> (loop with year = 2010 and month = 'november
>       for day from 1 to 30
>       as dow in '#1=(monday tuesday wednesday thursday friday saturday
>                      sunday . #1#)
>       collect (list dow month day year))

Is the syntax '#1= and #1# discussed in Common Lisp the Language?

Piotr Chamera

unread,
Nov 13, 2010, 4:53:36 AM11/13/10
to
W dniu 2010-11-13 08:20, TheFlyingDutchman pisze:

> Is the syntax '#1= and #1# discussed in Common Lisp the Language?

in cltl2 - 22.1.4
in hyperspec - 2.4.8 (2.4.8.15, 2.4.8.16)

Christophe Rhodes

unread,
Nov 13, 2010, 7:13:15 AM11/13/10
to
Tim Bradshaw <t...@tfeb.org> writes:

> On 2010-11-12 14:53:21 +0000, Zach Beane said:
>
>> Oh? Should (stringp (make-array 0 :element-type nil)) be T or NIL? Why
>> or why not? Do implementations agree with you, and with each other?
>
> It may be T or NIL (and I bet you know this :-).
>
> When creating an array an implementation may return an array
> specialised to any supertype of the requested element type. So,
> since all types are supertypes of NIL, the resulting array may have
> any element type at all, and this includes strings.

Not quite. :-) As /I/ bet Zach knows, there was a certain kerfuffle
when Paul Dietz read the CLHS quite carefully in pursuit of a test
suite. Firstly, observe that implementations must support both
base-strings (arrays specialized to BASE-CHAR) and bit-vectors (arrays
specialized to BIT); this is mandated on CLHS
UPGRADED-ARRAY-ELEMENT-TYPE. Then, CLHS 15.1.2.1 describes the upgraded
array type system as a lattice:

Type upgrading implies a movement upwards in the type hierarchy
lattice. A type is always a subtype of its upgraded array element
type. Also, if a type Tx is a subtype of another type Ty, then the
upgraded array element type of Tx must be a subtype of the upgraded
array element type of Ty. Two disjoint types can be upgraded to the
same type.

So, now let's consider Tx as NIL in this paragraph. NIL is a subtype of
BIT, so the upgraded array element type of NIL must be a subtype of the
upgraded array element type of BIT, which is BIT. So
(subtypep (upgraded-array-element-type nil) 'bit)
must be true. By the same reasoning,
(subtypep (upgraded-array-element-type nil) 'base-char)
must be true. But since BIT and BASE-CHAR are disjoint, the only way to
satisfy this is for (upgraded-array-element-type nil) to be NIL.

Therefore, conforming implementations must support arrays specialized to
NIL, and Zach's form must return T in conforming CL implementations.

Christophe

Tim Bradshaw

unread,
Nov 13, 2010, 10:25:09 AM11/13/10
to
On 2010-11-13 07:20:02 +0000, TheFlyingDutchman said:

> It would be nice if the next standard did it internally in an
> efficient manner. An abstract list type or new type LIST-NODE that has
> new functions like add-to-list that do not allow the creation of LIST-
> NODEs that are dotted or circular.

If you want this type, define it.

Tim Bradshaw

unread,
Nov 13, 2010, 10:42:43 AM11/13/10
to
On 2010-11-13 12:13:15 +0000, Christophe Rhodes said:

> Therefore, conforming implementations must support arrays specialized to
> NIL, and Zach's form must return T in conforming CL implementations.

OK, that sounds right. I think that additionally they only have to (or
can in fact) support arrays or zero dimension with element type NIL
That's still probably an unfortunate requirement though, so I guess I
now think this is a problem in the spec. Is there an easy fix? It
seems to me that disallowing NIL as the requested type would work, but
it might have other ramifications I've not thought of.

Duane Rettig

unread,
Nov 13, 2010, 11:27:57 AM11/13/10
to

It is. The spec doesn't specify that an array of element-type nil is
a subtype of any array or element-type X, even though nil is by
definition a subtype of X, even though it is intuitively not true. So
when the spec says that a string is any array with element-type which
is a subtype of character, there is ambiguity as to whether array of
element-type nil is a string or not, because then you would also have
to call an array of element-type nil a bit vector also (because its
element-type is a subtype of bit), etc. This was a failure to specify
if the string case is a special case where nil is included, or whether
the nil type is a special case which doesn't extend its subtyping in
arrays it makes.

Duane

Duane Rettig

unread,
Nov 13, 2010, 11:32:49 AM11/13/10
to
On Nov 12, 11:14 am, p...@informatimago.com (Pascal J. Bourguignon)
wrote:

Interesting coding, but not very interesting, which proves my point.
Any object can be used for the eof object that isn't likely to be
returned from the read, which includes any freshly-consed object. The
usual way to code this, though, is to use the stream itself; it is not
only not likely to be returned by read, it is much more likely to be
understood as an eo object (by convention) than an object that could
be indistinguishable from garbage in the reading context.

Duane

Pascal J. Bourguignon

unread,
Nov 13, 2010, 12:20:29 PM11/13/10
to
Duane Rettig <du...@franz.com> writes:

Sure. This was just an example. You may need an object with unique
identity in other contexts where you don't have a stream handy.

My opinion is that it's interesting in a theorical point of view, for
the completeness of the type lattice.

(make-array 0 :element-type nil) is a string, but it should also be an
element of all vector types, including bit-vector (of dimension 0).

It seems to me that implementations that return nil (all of them it
seems) on (typep (make-array 0 :element-type nil) 'bit-vector) are in
error, and while they can print (make-array 0 :element-type nil) as "",
they could as well print it as #0* or #(), etc.

Madhu

unread,
Nov 13, 2010, 1:04:34 PM11/13/10
to
* (Pascal J. Bourguignon) <87wroht...@kuiper.lan.informatimago.com> :
Wrote on Sat, 13 Nov 2010 18:20:29 +0100:

| Duane Rettig <du...@franz.com> writes:
|>>
|>> (loop with eof = (make-array 0 :element-type nil)
|>> for d = (read stream nil eof)
|>> until (eql d eof)
|>> do ...)
|>
|> Interesting coding, but not very interesting, which proves my point.
|> Any object can be used for the eof object that isn't likely to be
|> returned from the read, which includes any freshly-consed object.
|> The usual way to code this, though, is to use the stream itself; it
|> is not only not likely to be returned by read, it is much more likely
|> to be understood as an eo object (by convention) than an object that
|> could be indistinguishable from garbage in the reading context.
|
| Sure. This was just an example. You may need an object with unique
| identity in other contexts where you don't have a stream handy.


I believe you are missing Duanne's point. I don't think there is
anything preventing any implementation from returning the same object
for (make-array 0 :element-type nil)---an implementation can optimize
this with by returning the same immutable.

So the category of uses you alluded to where you want an empty object
with identity may as well not be served by an object of this type by any
conforming implementation; the spec makes no guarantees about it as it
does not talk about this type of object at all.

| My opinion is that it's interesting in a theorical point of view, for
| the completeness of the type lattice.

--
Madhu

TheFlyingDutchman

unread,
Nov 13, 2010, 1:51:39 PM11/13/10
to

> > Is the syntax '#1= and #1# discussed in Common Lisp the Language?
>
> in cltl2 - 22.1.4
> in hyperspec  - 2.4.8 (2.4.8.15, 2.4.8.16)

Thanks!

Tim Bradshaw

unread,
Nov 13, 2010, 2:11:57 PM11/13/10
to
On 2010-11-13 18:34:34 +0000, Madhu said:

> I believe you are missing Duanne's point. I don't think there is
> anything preventing any implementation from returning the same object
> for (make-array 0 :element-type nil)---an implementation can optimize
> this with by returning the same immutable.

I think it would be pretty odd if any two calls to MAKE-ARRAY return
the same object (certainly the wording in the spec is "creates and
returns").

Tim Bradshaw

unread,
Nov 13, 2010, 2:31:16 PM11/13/10
to
On 2010-11-13 16:27:57 +0000, Duane Rettig said:

> It is. The spec doesn't specify that an array of element-type nil is
> a subtype of any array or element-type X, even though nil is by
> definition a subtype of X, even though it is intuitively not true. So
> when the spec says that a string is any array with element-type which
> is a subtype of character, there is ambiguity as to whether array of
> element-type nil is a string or not, because then you would also have
> to call an array of element-type nil a bit vector also (because its
> element-type is a subtype of bit), etc. This was a failure to specify
> if the string case is a special case where nil is included, or whether
> the nil type is a special case which doesn't extend its subtyping in
> arrays it makes.

I'm not sure I understand this. I do now see that there's this odd
thing where an array (of zero dimensions) whose *actual* element type
is nil must exist (which must be inconvenient for implementations), but
I think then it's obvious that indeed it is a string, and a bit-vector,
and so on.

It seems to me that - assuming it is implementationally painful to
provide such a type (I mean, what use is it?), the answer would be to
explicitly say that NIL is *not* a legal specifier for an array element
type. However I'm not sure if that does violence to the type system or
not.

Pascal J. Bourguignon

unread,
Nov 13, 2010, 3:04:22 PM11/13/10
to
Tim Bradshaw <t...@tfeb.org> writes:

> On 2010-11-13 16:27:57 +0000, Duane Rettig said:
>
>> It is. The spec doesn't specify that an array of element-type nil is
>> a subtype of any array or element-type X, even though nil is by
>> definition a subtype of X, even though it is intuitively not true. So
>> when the spec says that a string is any array with element-type which
>> is a subtype of character, there is ambiguity as to whether array of
>> element-type nil is a string or not, because then you would also have
>> to call an array of element-type nil a bit vector also (because its
>> element-type is a subtype of bit), etc. This was a failure to specify
>> if the string case is a special case where nil is included, or whether
>> the nil type is a special case which doesn't extend its subtyping in
>> arrays it makes.
>
> I'm not sure I understand this. I do now see that there's this odd
> thing where an array (of zero dimensions) whose *actual* element type
> is nil must exist (which must be inconvenient for implementations),
> but I think then it's obvious that indeed it is a string, and a
> bit-vector, and so on.

I don't see why it should not be a bit-vector. And a vector of points,
and a vector or double-floats and a vector of conses, etc.

(every (lambda (type) (subtypep 'nil type)) '(point double-float cons ...))
--> T

What is wrong is when (subtypep '(vector nil 0) 'bit-vector) returns
NIL. For this, you have indeed to do inconvenient things and special
cases in the implementations. I'm not sure CLHS specifies it either.


> It seems to me that - assuming it is implementationally painful to
> provide such a type (I mean, what use is it?), the answer would be to
> explicitly say that NIL is *not* a legal specifier for an array
> element type. However I'm not sure if that does violence to the type
> system or not.

It seems to me that making an exception on the NIL type would be more
painful to the implementation than allowing it.

There's nothing wrong with it, since the number of elements is 0, which
is exactly the number of different values the NIL type has.

Raffael Cavallaro

unread,
Nov 13, 2010, 3:19:41 PM11/13/10
to
On 2010-11-13 00:50:11 -0500, TheFlyingDutchman said:

> If the Lisp community reaches out and embraces cargo cult
> programmers

You do know that the term "cargo cult programer" is a pejorative, right?

A cargo cult is a religion among native south sea peoples who think
ritual activities will induce the return of cargo laden ships and
planes to the area. Since the second world war this has most frequently
taken the form of building mock airfields with mock control towers and
mock aircraft, all from bamboo, wood, leaves and vines in the vain hope
that these mock airfields will magically cause the airmen and sailors
who actually were in the region during WWII to return, bringing goodies
- the eponymous "cargo"- such as Hershey bars and Spam (real spam, not
email) with them.

By definition, cargo cult programmers cannot be lisp programmers - a
cargo cult programmer is one who doesn't understand what he is doing -
i.e., he programs by rote substitution, copy-paste, etc., not by
actually understanding the semantics of what he is writing.

To become a lisp programmer is to cease being a cargo cult programmer.
A lisp programmer understands what he is doing - it's impossible to
master the language without understanding the semantics of it.

warmest regards,

Ralph

--
Raffael Cavallaro

Tim Bradshaw

unread,
Nov 13, 2010, 3:54:44 PM11/13/10
to
On 2010-11-13 20:04:22 +0000, Pascal J. Bourguignon said:

> [things that I think mean we are agreeing?]

> It seems to me that making an exception on the NIL type would be more
> painful to the implementation than allowing it.
>
> There's nothing wrong with it, since the number of elements is 0, which
> is exactly the number of different values the NIL type has.

My problem (and I'd defer to implementors on this, of course) is that
in order for this to work you actually need this perculiar specialised
array type for an element type of NIL and maybe such a type is
expensive in terms of using up bits in representations of types and so
on (and implementation effort), for very little gain. If that is not
the case, then I'd agree with you that yes, this type should exist (and
then it turns out that there isn't a problem with the spec I think).

TheFlyingDutchman

unread,
Nov 13, 2010, 4:18:14 PM11/13/10
to

>
> You do know that the term "cargo cult programer" is a pejorative, right?
>
I had not heard the term until it was used here recently, but I did
Google it. To me it sounds silly and I used it tongue in cheek.

Madhu

unread,
Nov 13, 2010, 8:17:25 PM11/13/10
to

* Tim Bradshaw <ibmntu$1l9$1...@news.eternal-september.org> :
Wrote on Sat, 13 Nov 2010 19:11:57 +0000:

I believe you are missing the point too. Despite being odd (to your
eyes) it would still be conforming, the object cannot be changed or used
for any purpose other than for testing its type, the optimization would
be similar to other optimizations that implementations routinely do.

--
Madhu

Tim Bradshaw

unread,
Nov 14, 2010, 4:57:01 AM11/14/10
to
On 2010-11-14 01:47:25 +0000, Madhu said:

> I believe you are missing the point too. Despite being odd (to your
> eyes) it would still be conforming, the object cannot be changed or used
> for any purpose other than for testing its type, the optimization would
> be similar to other optimizations that implementations routinely do.

It can be used for another purpose: its identity.

Madhu

unread,
Nov 14, 2010, 5:50:43 AM11/14/10
to

* Tim Bradshaw <ibobpd$f9s$1...@news.eternal-september.org> :
Wrote on Sun, 14 Nov 2010 09:57:01 +0000:

Yes You have clearly missed the point.

The purpose of my post (which was for Pascal's edification) was to point
out that it cannot be used for identity.

But arguing with you is like like arguing with a java-fruit-salad
schemer who just does not understand common lisp.

--
Madhu

Tim Bradshaw

unread,
Nov 14, 2010, 6:15:56 AM11/14/10
to
On 2010-11-14 11:20:43 +0000, Madhu said:

> Yes You have clearly missed the point.
>
> The purpose of my post (which was for Pascal's edification) was to point
> out that it cannot be used for identity.

It can not be used for its identity *if and only if* you allow
MAKE-ARRAY to return arrays which are EQ to each other in some cases.
There's no other case when it can do this, and I don't think it can do
this in this case either. I'd be interested in your argument as to why
it can (other than just baldly stating that it can which is all you
have done so far), although I suspect you are too busy having another
tantrum now.

Tamas K Papp

unread,
Nov 14, 2010, 10:08:44 AM11/14/10
to

I think that this issue is pretty clear: the HS page for MAKE-ARRAY
calls the returned array "new-array", which obviously means that it
is, well, new. Also, the description is "CREATES and returns an array
CONSTRUCTED" (my upper case), which also suggests that this array is
freshly created.

I don't know if the HS covers this explicitly anywhere, but I was
under the impression that CONS, MAKE-ARRAY, MAKE-SEQUENCE, etc always
create objects which are not EQ to anything else that existed before
they were called. The standard has the concept of "fresh" [1], but it
is mentioned explicitly only in a few cases. Maybe I am wrong, but I
always assume that objects which are "created" are "fresh".

Tamas

[1] http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_f.htm#fresh

Robert Maas, http://tinyurl.com/uh3t

unread,
Nov 15, 2010, 12:20:56 PM11/15/10
to
> From: t...@sevak.isi.edu (Thomas A. Russ)
> (defun primes ()
..
> (let ((memo (make-array 1000000001 :element-type 'bit :initial-element 0)))
> ...
> ;; Note, this should probably return MEMO, but for
> ;; algorithm performance testing, we return T to
> ;; avoid having the REPL print a big array.
> t))

Nice work by Mr. Russ.
Tiny suggestion for improvement:
At the top, declare (defvar $memo$)
Just before returning T, do (setq $memo$ memo)
That way you can still time just the computation without the print
of large array, but you have the large array bound to a global so
that you can later browse it to see whether the algorithm actually
worked correctly, and maybe actually use the result for something.

For self-documenting user interface, instead of returning T, return
the constant-string "See result in global $memo$" or somesuch.
Don't print or format that string because then the time to print
would be included in the timing info. You want just the calculation
of the array by itself (well the allocation of the array is also
included in the timing; I don't know a good way to allocate the
array before starting to time, while still taking advantage of the
localness of the optimization to get maximum speed).

Note for newbies: Unlike C, where arrays defined inside functions
are typically allocated on the stack, thus dissappear upon function
return, make-array allocates on the heap, so setting the global is
OK. Calling make-array is roughly equivalent to calling malloc or
calloc etc., which is a pain in C, whereas calling make-array in
Lisp is programmer friendly.

Robert Maas, http://tinyurl.com/uh3t

unread,
Nov 15, 2010, 12:57:32 PM11/15/10
to
> From: Ariel Badichi <vermilionr...@gmail.com>
> It looks like both you and Andy Venikov are using 32-bit SBCL.
> Here's what I get on a 64-bit SBCL:
> CL-USER> array-total-size-limit
> 1152921504606846973
> CL-USER> most-positive-fixnum
> 1152921504606846975

For comparison, what I'm using here here:

FreeBSD/amd64 (shell0.rawbw.com)

Copyright (c) 1992-2010 The FreeBSD Project.
Copyright (c) 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994
The Regents of the University of California. All rights reserved.
FreeBSD 7.3-RELEASE-p2 (SHELL0) #0: Sun Aug 29 00:42:44 PDT 2010

CMU Common Lisp 19c Release (19C), running on shell0.rawbw.com
With core: /usr/local/lib/cmucl/lib/lisp.core
Dumped on: Wed, 2005-11-16 17:18:11-08:00 on snapdragon.csl.sri.com
See <http://www.cons.org/cmucl/> for support information.
Loaded subsystems:
Python 1.1, target Intel x86
CLOS based on Gerd's PCL 2004/04/14 03:32:47

array-total-size-limit => 536870911
most-positive-fixnum => 536870911

*sigh*

I have only 10 MB of unused disk space at the moment, so I can't
download version 19f to see if it makes better use of the 64-bit
CPU. (I downloaded 19f a year or two ago to try something, and it
did have two different coreimages, for 32-bit and 64-bit, but I had
to delete it all to make room for 66 MB of images rescued when
Yahoo GeoCities shut down plus an additional 115 MB of new images
I've been processing since then. In September I had to stop work on
my image-processing software because there's no space left to write
new images to the disk.)

Robert Maas, http://tinyurl.com/uh3t

unread,
Nov 15, 2010, 1:52:11 PM11/15/10
to
> From: p...@informatimago.com (Pascal J. Bourguignon)
> TheFlyingDutchman <zzbba...@aol.com> writes:
> > Anyway, it's clear that nil is nil and nil is null, but null isn't
> > nil. Showing that nil is more flexible than null. ;)

Note the unsmiley, he was telling a(n admittedly stupid) joke.

> No. NIL is not NULL. NIL *IS-A* NULL.

> (eq 'nil 'null) --> nil
> (typep 'nil 'null) --> t

Good explanation, but then:

> You seem to have a hard time with some quite basic concepts...

Ouch! That seems a bit harsh for a newbie who was just making a joke.

By the way, every so often somebody asks me whether I'm a
"programmer", in order to pidgenhole me into a category as a
special-purpose device, a one-trick pony, whose sole ability is to
"program". I usually explain that I'm not a single-purpose
programming device. I'm a *human*being*, which can do *lots* of
things, including:
- Conceiving new needs for society that nobody ever thought of before.
- Conceiving new solutions for such new needs, and for old needs
nobody could solve before.
- Assessing whether my new solutions would be feasible with current
or soon-upcoming technology.
- Devising a general plan for implementing such a new solution,
usually either by a pure software solution or a cybernetic
(human + computer components integrated together) solution.
- Diagnosing key components of the plan that *might* not really
work, then devising tests of whether they are possible using any
programming tricks I can think of.
- Designing specific software components together with a timeline
for implementation and benchmarks.
- Writing code for each component, unit-testing each line of code
as I write it, unit-testing each completed function, testing
everything so-far from input to the last completed stage.
Unfortuately most people don't have the patience to listen to my
explanation why "yes" or "no" isn't an appropriate answer to their
stupid-in-the-first-place question.

Note that if I say "yes", the person responds "sorry, this job
isn't for a programmer, it's for a software engineer", or it's for
some other specific job category that fits some of what I can do.

Note that if I say "no", the person responds "oh, I thought you
wrote software, I guess I was mistaken".

Does anybody have an idea how to respond to the "are you a
programmer" question that doesn't get me immediately rejected for
the job?

Does anybody know of any job near San Jose (California) that
involves Lisp or PHP/MySQL programming but doesn't require a degree
in computer science (when I attended college there was no such
degree in existance) nor require 5 years J2EE with Hibernate and
WebSphere and JBoss?

By the way, a few days ago I finished implementing my method for
covertly bootstrapping parameters for a per-site-custom public-key
cryptosystem across an insecure link to a remote site.

There's a chicken-and-egg problem involved. There is *no*
cryptosystem available on the remote system by which it can decrypt
public-key-encrypted data sent to it, so there's no way to encrypt
the public-key parameters you want to send to it to tell it how to
decrypt the stuff you want to send it covertly. I solved this
problem.

In the course of working out this solution, I also solved the
problem of generating a *truly* random seed without access to any
hardware noise source, hence I have a pure PHP function that
generates a *truly* random seed with sufficient entropy to
guarantee covertness of the PK-boot process. (I also adapted a
slight variation of this algorithm to work in Lisp.)

Pascal J. Bourguignon

unread,
Nov 15, 2010, 1:57:43 PM11/15/10
to
seeWeb...@rem.intarweb.org (Robert Maas, http://tinyurl.com/uh3t)
writes:

> Note for newbies: Unlike C, where arrays defined inside functions
> are typically allocated on the stack, thus dissappear upon function
> return, make-array allocates on the heap, so setting the global is
> OK. Calling make-array is roughly equivalent to calling malloc or
> calloc etc., which is a pain in C, whereas calling make-array in
> Lisp is programmer friendly.

(let ((a (make-array 3 :initial-contents '(1 2 3))))
(declare (dynamic-extent a0))
(aref a 0))

Now the compiler can allocate A on the stack.

Zach Beane

unread,
Nov 15, 2010, 2:01:35 PM11/15/10
to

> Note for newbies: Unlike C, where arrays defined inside functions


> are typically allocated on the stack, thus dissappear upon function
> return, make-array allocates on the heap, so setting the global is
> OK. Calling make-array is roughly equivalent to calling malloc or
> calloc etc., which is a pain in C, whereas calling make-array in
> Lisp is programmer friendly.

I don't think newbies these days will be enlightened by describing how
Lisp is different from C. More likely they will be thinking in terms of
how Java, C#, JavaScript, Perl, Python, or Ruby work. Malloc? Who uses
that any more?

Zach

Robert Maas, http://tinyurl.com/uh3t

unread,
Nov 15, 2010, 2:04:13 PM11/15/10
to
> >> No. NIL is not NULL. NIL *IS-A* NULL.
> > And no objects are of type NIL (it is the bottom of the type graph),

> From: Tamas K Papp <tkp...@gmail.com>
> I learned this after getting a neat error message in SBCL after
> typing (make-array nil :element-type nil) in the REPL.
> Clearly, the designers of ANSI CL really thought about all the
> corner cases.

Sigh, CMUCL 19c doesn't detect the erroneous request:
(make-array nil :element-type nil) => #0A#\Null
Does anybody have 19f to see if it's been "fixed" to signal an
error instead of doing GIGO?
(Was it Jon Postel who recommended that software should accept
garbage in and guess something the user might want instead of
signalling an error? He was speaking of RFC protocols, but the
same mistaken allow-GIGO policy could be cited equally badly here.)
(I have only 10MB free disk space so I can't download 19f and try
it myself for this case, sigh.)

Pascal J. Bourguignon

unread,
Nov 15, 2010, 2:19:43 PM11/15/10
to

>> >> No. NIL is not NULL. NIL *IS-A* NULL.

Perhaps you could compress some less used files?

TheFlyingDutchman

unread,
Nov 15, 2010, 2:23:26 PM11/15/10
to

> Malloc? Who uses that any more?

According to the TIOBE index there are still a lot of C and C++
programmers out there. Those languages are number two and three per
it's criteria for language popularity.

TheFlyingDutchman

unread,
Nov 15, 2010, 2:30:34 PM11/15/10
to

> Unfortuately most people don't have the patience to listen to my
> explanation why "yes" or "no" isn't an appropriate answer to their
> stupid-in-the-first-place question.
>
> Note that if I say "yes", the person responds "sorry, this job
> isn't for a programmer, it's for a software engineer", or it's for
> some other specific job category that fits some of what I can do.
>
> Note that if I say "no", the person responds "oh, I thought you
> wrote software, I guess I was mistaken".
>
> Does anybody have an idea how to respond to the "are you a
> programmer" question that doesn't get me immediately rejected for
> the job?
>
From what you have said I would respond with "I am a software
engineer, and quite proficient in programming."

TheFlyingDutchman

unread,
Nov 15, 2010, 2:38:39 PM11/15/10
to

> At the top, declare (defvar $memo$)
> Just before returning T, do (setq $memo$ memo)

Do $'s around a variable have a standard meaning or is that just your
personal convention?

Robert Maas, http://tinyurl.com/uh3t

unread,
Nov 15, 2010, 3:21:33 PM11/15/10
to
> >> ... Should (stringp (make-array 0 :element-type nil)) be T or NIL? Why
> >> or why not? Do implementations agree with you, and with each other?

> From: Tamas K Papp <tkp...@gmail.com>

> I don't really see why implementations should agree on this.
> :element-type justs requests an array element type, and it may be
> upgraded if the implementation doesn't support that specialized type.
> NIL is not in 15.1.2.2, and since nil is a subtype of every type [1],
> the implementation is free to return an array of any element type.
> Depending on whether this upgraded array element type is a subtype of
> char or not, stringp can return nil or t.
> Is this interpretation incorrect?

From a mathematical point of view (I placed top-five nationwide in
the William Lowell Putnam competitition, so I think I'm qualified
to express an expert opinion here), I must agree with Mr. Papp.

For best user friendliness in the case the user asks such an absurd
task to be performed, I would favor signalling an error, but
technically creating an array of J. Random type is legal per ANSI
specification.

> I don't see how the implementations could agree on the result of
> (stringp (make-array 0 :element-type nil)) without either requiring
> NIL in 15.1.2.2, or making it a special case for make-array.

I would propose an alternate mod to the spec: Whenever there are
more than one equally best upgrades from the specified type to some
actually-implemented type, an error *should* be signalled, rather
than guessing which type the user really wants. Thus upgrading from
(integer 5 14) to (integer 0 255) is fine, but upgrading from NIL
to J. Random type *should* signal an error.

Of course a Draconian implementor could, per the current spec,
upgrade NIL to (integer 0 (expt 2 (expt 10 (expt 10 100))))
i.e. a googolplex number of bits per integer value, and then quite
legitimately signal an error that all memory has been exhausted
trying to allocate a single element of that type.

> Where can I get clall? (Google kindly gives me the Crystal Lake
> American Little League website).

Google gives me:
- 1,2 = Crystal Lake American Little League
- 3 = www.acronymfinder.com which has the above as the *only*
expansion, but *you* can volunteer to suggest an additional
definition, right?
Link to www.acronymattic.com/CLALL.html which has nothing more.
- 4 = Claudia Maria = www.clall.com
- 5 = Fast Track Clall Taxi Complaints
- 6 = (some car-trade-in event)
- 7,8 = (some torrent/warez site)
- 9 = (mis-transcription of a PDF file about Minnesota crime)
- 10 = (another warez site)

One of the initiatives I've proposed, and would like to work on if
anyone else shows enough interest, is a formal registry of:
- Individual people
- Species/clades of life
- Common nouns of types of inanimate objects
- Locations, both on Earth and other explored bodies, and in the
Celestial Sphere.
- Events
A search engine per these classifications would then let the user
browse the disambiguation page and click on the desired specific
register thing/place, which then presents a page of search-results
referring to just that one registered thing/place rather than a
mixmash of everything by the same name as Google does currently.

The people registery would identify people by face, name, and
public information such as my placing top five nationwide in the
Putnam contest, or Heather Thompson Bolles being the Master of
School Mathematics (MSM) program director at Iowa State University.
It would cluster people according to similar names and
similar-looking faces. Thus Tina Fey (actress on SNL) would be
clustered with Sarah Palin (former governor of Alaska) by facial
similarity. The name "Heather Thompson" (several hundred people by
that same name) would be clustered with "Heather Thomson" (inventor
of Yummy Tummy) per name similarity (and several Web pages that
have her family name misspelled as "Thompson").

The classification of items, as well as the clustering of face
similarities, would be per a cybernetic algorithm, whereby humans
are paid to identify which meaning of a textual term is intended by
the WebPage or other item being classified, or paid to identify who
looks very similar to who else in a set of "mug shots". A "reverse
tree" vetted by a "truth futures" market would assure quality
control, unlike a Wiki which allows unvetted free-for-all edits to
the database, unlike Google's image tagging "game" which doesn't
really work well at all and doesn't even *attempt* to truly
classify word usage per ambiguous meanings.

Perhaps I should also add a registry of meanings of
abbreviations/acronyms within this system. There already is an
acronym [sic] database online, but it just shows the textual
expansions of the abbreviation as a dead-end, doesn't group
Google-style search results per this classification.

On a related topic per my Google results and misspelling of Heather
Thomson's name, I plan to include as part of
http://TinyURL.Com/NewEco a poll as to which are the most offensive
typos or mistakes or lies on the Web and then pay people to pester
the Web authors to quickly fix their errors, and then to re-submit
their corrected Web pages to Google so that Google will stop
serving their pages under the misspelling.

Note: Harvesting of content from Web pages will be done by Lisp,
while the user interface to the search engine and results will be
done by PHP/MySQL. I'm already using this combination of Lisp and
PHP/MySQL for http://TinyURL.Com/VTAorg which lets cell-phone users
select and view the information they need about public-transit
schedules.

Thomas A. Russ

unread,
Nov 15, 2010, 3:33:33 PM11/15/10
to
TheFlyingDutchman <zzbb...@aol.com> writes:

>> [Proper Lists]
>
> It would be nice if the next standard did it internally in an
> efficient manner. An abstract list type or new type LIST-NODE that has
> new functions like add-to-list that do not allow the creation of LIST-
> NODEs that are dotted or circular.

Why? What is the real benefit of doing this? Right now you have to
specifically create dotted pairs or circular lists. If you don't want
any, then don't create them!

Plus, I'm not sure it would be at all easy to determine if you are
building a circular list. Do you have an idea for an O(1) algorithm
that can figure out if what you are adding to the list will make it
circular?

I suppose that if you never allowed any non-fresh nodes to be added you
could achieve this. But that would seem to make these data structuers a
lot more cumbersome to use and you would eliminate the ability to do any
structure sharing among the list structures. That would seem to make a
number of efficiency techniques inapplicable.

Besides, you could always easily create your own interface that would do
what you want.

> > Circular lists will often give fits to the sequence functions as well.
> > OK, I guess techincally you won't get an error from using a circular
> > list, but you still often won't like the results.  Although I can think
> > of some circumstances where they can be used.  Consider
> >
> > (loop with year = 2010 and month = 'november
> >       for day from 1 to 30
> >       as dow in '#1=(monday tuesday wednesday thursday friday saturday
> >                      sunday . #1#)
> >       collect (list dow month day year))


>
> Is the syntax '#1= and #1# discussed in Common Lisp the Language?

Yes.

It should be in the section on the reader macros.
In my copy of CLtL2 it is in p. 537.

But bear in mind that CLtL2 is a reference manual and not a tutorial, so
don't be surprised if the treatment is cursory with the wider
implications left to the reader.

--
Thomas A. Russ, USC/Information Sciences Institute

Thomas A. Russ

unread,
Nov 15, 2010, 3:41:53 PM11/15/10
to
TheFlyingDutchman <zzbb...@aol.com> writes:

I've seen them used to denote constants rather than variables. In that
case, they would properly be introduced by

(defconstant $buffer-size$ 1024)

Everyone else uses *s around special variables. This is more common
than the $ convention for constants.

Robert Maas, http://tinyurl.com/uh3t

unread,
Nov 15, 2010, 4:27:04 PM11/15/10
to
> From: Christophe Rhodes <cs...@cantab.net>
> Then, CLHS 15.1.2.1 describes the upgraded array type system as a lattice:
| Type upgrading implies a movement upwards in the type hierarchy
| lattice. A type is always a subtype of its upgraded array element
| type. Also, if a type Tx is a subtype of another type Ty, then the
| upgraded array element type of Tx must be a subtype of the upgraded
| array element type of Ty. Two disjoint types can be upgraded to the
| same type.

Ouch! So if the user specifies type Tx, then whatever it's upgraded
to must be a sub-type of *every* super-type of Tx. Thus if Tx is
NIL, it can only be upgraded to the intersection of two disjoint
types (such as base-char and bit for example), which is the type
NIL itself. It *cannot* legally be upgraded to any higher type.

> So, now let's consider Tx as NIL in this paragraph. NIL is a subtype of
> BIT, so the upgraded array element type of NIL must be a subtype of the
> upgraded array element type of BIT, which is BIT. So
> (subtypep (upgraded-array-element-type nil) 'bit)
> must be true. By the same reasoning,
> (subtypep (upgraded-array-element-type nil) 'base-char)
> must be true. But since BIT and BASE-CHAR are disjoint, the only way to
> satisfy this is for (upgraded-array-element-type nil) to be NIL.

Yes. I read this before writing my alternate explanation just above
it. I hope my explanation is more clear for some readers, and vice
versa, thus both explanations should be kept in this thread.

> Therefore, conforming implementations must support arrays
> specialized to NIL, and Zach's form must return T in conforming CL
> implementations.

I agree, and retract my earlier agreement with the claim that the
spec allows upgrading to J. Random supertype of NIL, or at least J.
Random minimal supertype of NIL.

In the case of either of the cited forms:
(make-array 0 :element-type nil)
(make-array nil :element-type nil)
in either case the there is nothing whatsoever that can be an
element, but since the array doesn't need to be initialized with
even one element this is not a problem? But if at least one element
is required, such as:
(make-array 1 :element-type nil)
there is no conforming way to satisfy the user's requirement.

Note: In CMUCL 19c:

(make-array 0 :element-type nil) => ""
(type-of *) => (SIMPLE-BASE-STRING 0)
(length **) => 0
;That might be wrong. Opinion?

(make-array nil :element-type nil) => #0A#\Null

(type-of *) => (SIMPLE-ARRAY BASE-CHAR NIL)
(length **) ===>
Type-error in KERNEL::OBJECT-NOT-TYPE-ERROR-HANDLER:
#0A#\Null is not of type SEQUENCE
;That might be correct in operational effect, even though type-of
; shows the (moot) element type is wrong. Opinion?
;(I have a bag of zero unicorns? True! Unicorn is wrong type, I
; asked for a bag of zero mice, not zero unicorns, but since there
; are zero of them, the type of each is moot, and I really don't
; care whether the bag is labeled as containing zero mice or zero
; unicorns so long as it has nothing whatsoever of any type
; whatsoever in it. Now if the bag is delivered with a Schroedinger
; 0.01 of a black hole, that would start to worry me.)

(make-array 1 :element-type nil) => ""
(type-of *) => (SIMPLE-BASE-STRING 1)
(length **) => 1
(map 'list #'char-code ***) => (0)
;That seems *clearly* non-conforming to me.
;But in this case it is impossible for *any* implementation to be
; conforming. Opinion?

;Well, actually a *lazy* implementation *could* possibly be
; conforming. Instead of making an array that actually has one
; element of type nil, which is impossible, because nothing is of
; type nil, it's yield a continuation where if you ask for the array
; type you get that it's an array containing one element of type NIL,
; but if you ask for that particular array element *then* it signals
; an error that it can't produce such an element to show you
; because it's impossible for there to be any such element.
;Does the ANSI CL spec allow lazy implementations?


Perhaps it's time to go one step further towards fully acnowledging
that intentional and implemetational datatypes are *not* the same.
When the user asks for something, that's the intentional datatype,
but when the system provides something to satisfy his/her need,
that's the (upgraded) implementational datatype. Perhaps in some
future version of Lisp, arrays and other upgradable objects would
be required to carry along with them both the datatype the user
asked for and the datatype they were upgraded to.

(type-of (make-array 5 :element-type '(integer 5 42)))
=>
(SIMPLE-ARRAY (UNSIGNED-BYTE 8) (5)) ;First return value, implementational type
(SIMPLE-ARRAY (INTEGER 5 42) (5)) ;Second return value, intentional type

(type-of (get-reservation :airline :SOUTHWEST :flight 42 :class :COACH))
=>
:FIRST-CLASS ;Got lucky, upgraded because coach was full but 1st had vacancy
:COACH ;What person paid for, and what flight attendants might serve

Furthermore, OO-style programming would have a whole database of
what upgradings were possible. When merging collections of multiple
element types, individual elements would be tagged as to both their
intended type and their upgraded type, intended type on each
element individually but upgraded type tagged on the container as a
whole.

+-theBox-+
| inside | outside my thinking process
+--------+ (for dyslexics: this is **not** left field!!)

Tim Bradshaw

unread,
Nov 15, 2010, 4:32:13 PM11/15/10
to
On 2010-11-14 15:08:44 +0000, Tamas K Papp said:

> think that this issue is pretty clear: the HS page for MAKE-ARRAY
> calls the returned array "new-array", which obviously means that it
> is, well, new. Also, the description is "CREATES and returns an array
> CONSTRUCTED" (my upper case), which also suggests that this array is
> freshly created.

I agree with this. I think it's confusing that the only "obviously
immutable" things do have special restrictions on them around identity
(there's only one NIL, characters may or may not be EQ, numbers have
odd behaviour). But I don't think that this justifies treating this
special weird array type like you'd treat NIL copier with NIL but not
with an array, of whatever kind, I think). Especlally as there turn
out to be, possibly, other immmutable arrays: in an implementation
which supports arrays which are not adjustable (I think implementationd
do not have to) then (make-array 0 :adjustable nil ...) is immutable.

And it gets worse (or better). There's an unbounded number of *types*
of immutable object, I think, because, after (defstruct x), (make-x)
returns something immutable (I think, though implementations which
support structure redefinition might allow it to be mutated). Suddenly
they're everywhere.

This whole identity thing is very interesting I think: I guess
functional programming people may think it's not, because if nothing is
mutable then does identity really matter? But I kind of like these
objects which are essentially pure identity.

TheFlyingDutchman

unread,
Nov 15, 2010, 4:47:50 PM11/15/10
to

>
> Why?  What is the real benefit of doing this?  Right now you have to
> specifically create dotted pairs or circular lists.  If you don't want
> any, then don't create them!

Theoretically, I could be writing code that is called by someone else.
I would like to verify
they have passed a proper list if that is what my function requires.

>
> Plus, I'm not sure it would be at all easy to determine if you are
> building a circular list.  Do you have an idea for an O(1) algorithm
> that can figure out if what you are adding to the list will make it
> circular?

>
> I suppose that if you never allowed any non-fresh nodes to be added you
> could achieve this.  But that would seem to make these data structuers a
> lot more cumbersome to use and you would eliminate the ability to do any
> structure sharing among the list structures.  That would seem to make a
> number of efficiency techniques inapplicable.

It would be an alternative to CONS cells and related functions
(functions which force
proper list construction), not a replacement. Which, admittedly,
would
create confusion if a standard part of the language.

>
> Besides, you could always easily create your own interface that would do
> what you want.
>

I don't know about easily, but I guess I'll give it a try.

TheFlyingDutchman

unread,
Nov 15, 2010, 4:49:37 PM11/15/10
to

>
> I've seen them used to denote constants rather than variables.  In that
> case, they would properly be introduced by
>
Somewhere (CtL2?), I saw "+" being advocated to surround constants.

Raymond Toy

unread,
Nov 15, 2010, 5:01:55 PM11/15/10
to
On 11/15/10 2:04 PM, Robert Maas, http://tinyurl.com/uh3t wrote:
>>>> No. NIL is not NULL. NIL *IS-A* NULL.
>>> And no objects are of type NIL (it is the bottom of the type graph),
>
>> From: Tamas K Papp <tkp...@gmail.com>
>> I learned this after getting a neat error message in SBCL after
>> typing (make-array nil :element-type nil) in the REPL.
>> Clearly, the designers of ANSI CL really thought about all the
>> corner cases.
>
> Sigh, CMUCL 19c doesn't detect the erroneous request:
> (make-array nil :element-type nil) => #0A#\Null
> Does anybody have 19f to see if it's been "fixed" to signal an
> error instead of doing GIGO?

No, it does not signal an error here.

Ray

Robert Maas, http://tinyurl.com/uh3t

unread,
Nov 15, 2010, 5:18:24 PM11/15/10
to
> From: Duane Rettig <du...@franz.com>
> ... the designers of the Ansi Common Lisp ... not perfect, and so
> there are some issues with the spec, including the one Zach
> pointed out, many of which are listed here:
> http://www.cliki.net/Proposed%20ANSI%20Revisions%20and%20Clarifications

* BOUNDP refers to "the function bound". It should be "the function
boundp".
-> http://www.lisp.org/HyperSpec/Body/fun_boundp.html
-> 404 not found.

* Un-deprecate REMOVE-IF-NOT, DELETE-IF-NOT.
-> http://www.lisp.org/HyperSpec/Body/fun_removecm__elete-if-not.html
-> 404 not found.
-> http://www.lisp.org/HyperSpec/Body/fun_removecm__elete-if-not.html
-> 404 not found.

> All that aside, the only interesting thing about an array of
> element- type nil is the fact that it is not interesting.

That's curious, but not really interesting.
Maybe per the Gvdel numbering (and hence ordering) of mathematical
statements, that's the smallest non-interesting number?
Actually, "interesting" is a matter of degree, not an absolute
yes/no property. Accordingly, perhaps that's the least-interesting
Gvdel number up to that point in the sequence?

How interesting do you-all find my proposal to replace Capitalism
with a new kind of economic system where the unit of currency is
(clock second of) labor time rather than $dollar$, thus allowing
nearly everyone to earn labor credits (by doing work for the
system) and then spend them to hire others, thus allowing nearly
everyone to hire others, thereby providing so many jobs 24/7 that
almost nobody goes without employment for longer than a few hours,
thus ending poverty, thus ending desperation that leads to crime
and terrorism and war?

Pascal Costanza

unread,
Nov 15, 2010, 5:34:10 PM11/15/10
to
On 15/11/2010 22:47, TheFlyingDutchman wrote:

>> Why? What is the real benefit of doing this? Right now you have to
>> specifically create dotted pairs or circular lists. If you don't want
>> any, then don't create them!
>
> Theoretically, I could be writing code that is called by someone else.
> I would like to verify
> they have passed a proper list if that is what my function requires.

Just document it. That's standard practice. We don't hear about problems
because of improper lists a lot. What Thomas tries to say is that you
have to make an explicit effort to create them. It doesn't just happen
by accident...


Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/

Robert Maas, http://tinyurl.com/uh3t

unread,
Nov 15, 2010, 6:00:01 PM11/15/10
to
> From: p...@informatimago.com (Pascal J. Bourguignon)
> ... Everywhere when a gensym is used but a symbol is not needed,
> or where a cons cell is created, but nothing to be stored there,
> a nil array would be a better way to get an empty object with
> identity.
> (loop with eof = (make-array 0 :element-type nil)
> for d = (read stream nil eof)
> until (eql d eof)
> do ...)

In CMUCL, (make-array 0 :element-type nil) upgrades to (make-string
0), so today's programmer might as well call (make-string 0)
instead of waiting for the standard to be improved and then wait
for all implementations to be conforming to the new standard.

But (make-array nil :element-type nil) => #0A#\Null
and (type-of *) => (SIMPLE-ARRAY BASE-CHAR NIL)
which isn't any better, just more insidious.

For ease in debugging, I think the best code is:
(loop with eof = (copy-seq "EOF")
for d = (read stream nil eof)
until (eql d eof)
do ...)
but in practice I usually do:
(loop with eof = (make-symbol "EOF")
for d = (read stream nil eof)
until (eql d eof)
do ...)
because that was before your posting whereby I now realize that
(copy-seq ...) is more efficient than (make-symbol), or is it?

* (time (copy-seq "EOF"))
; 16 bytes consed.
* (time (make-symbol "EOF"))
; 32 bytes consed.
Yes indeed copy-seq is better, of my two debug-friendly versions.
I'll switch to it in the future.

* (time (make-array 0 :element-type nil))
; Compiling LAMBDA NIL:
; In: LAMBDA NIL
; (MAKE-ARRAY 0 :ELEMENT-TYPE NIL)
; Note: Default initial element #\Null is not a NIL.
; Compiling Top-Level Form:
; Compilation unit finished.
; 1 note
; 16 bytes consed.
No better than my more-debug-friendly (copy-seq "EOF")

* (time (make-array nil :element-type nil))
; Compiling LAMBDA NIL:
; In: LAMBDA NIL
; (MAKE-ARRAY NIL :ELEMENT-TYPE NIL)
; --> LET SETF LET* MULTIPLE-VALUE-BIND LET
; ==>
; (MAKE-ARRAY 1 :ELEMENT-TYPE C::ELEMENT-TYPE)
; Note: Default initial element #\Null is not a NIL.
; Compiling Top-Level Form:
; Compilation unit finished.
; 1 note
; 48 bytes consed.
OUCH!! Even more costly than make-symbol. This way is not good today!

Somebody recently said (in the thread about Forth implementation of
self-balancing search trees) the nice thing about Lisp is that
there are so many different ways to accomplish the same task.
When some ways are more debug-friendly and/or less expensive than
others, this is especially true.

Just to make sure my preferred method really works:
(let* ((form '(copy-seq "EOF"))
(e1 (eval form))
(e2 (eval form)))
(values e1 e2 (eq e1 e2)))
=> "EOF"
"EOF"
NIL
Indeed, unique identity.

Andy Venikov

unread,
Nov 15, 2010, 6:13:54 PM11/15/10
to
On 11/12/2010 7:59 AM, Tim Bradshaw wrote:

> On 2010-11-12 12:16:31 +0000, Pascal J. Bourguignon said:
>
>> No. NIL is not NULL. NIL *IS-A* NULL.
>
> And no objects are of type NIL (it is the bottom of the type graph),
> while all objects are of type T (the top of the type graph). (You know
> this, I know: the OP may not).
>

That would make it: "NIL (the type) IS-A T (the type)", wouldn't it?

So, NIL really is T.

Tim Bradshaw

unread,
Nov 15, 2010, 6:34:13 PM11/15/10
to
On 2010-11-15 23:00:01 +0000, Robert Maas, http://tinyurl.com/uh3t said:

> In CMUCL, (make-array 0 :element-type nil) upgrades to (make-string
> 0), so today's programmer might as well call (make-string 0)
> instead of waiting for the standard to be improved and then wait
> for all implementations to be conforming to the new standard.

I'm not at all sure what one could do about this, though it certainly
seems very peculiar. It certainly is a weird consequence, but it's not
at all clear what a fix would be, at least to me. The only thing I can
think of is simply disallowing NIL as a type-specifier to MAKE-ARRAY,
but that sounds like a weirdo special case in turn. Certainly if the
type graph has a bottom element (which I think is kind of nice as
otherwise it kind of peters out, and I think may be if you think of
types as sets of objects, you need the empty set, which is what this
bottom is), then I think that that, combined with the rules for
upgraded element types (which seem to me to be very hard to argue
with), means you end up with this weird type of array.

No one (that I've seen) has answered whether simply disallowing NIL as
a type specifier in MAKE-ARRAY would be OK (and I guess in other cases
where you are trying to specify the type of an actual thing: (defun foo
(x) (declare (type nil x)) ...) is pretty weird. I can't work out
whether it would be, or whether it would have some other bad
consequence. The flip side of that is: what is the cost to
implementations of supporting this weird array type?

> But (make-array nil :element-type nil) => #0A#\Null
> and (type-of *) => (SIMPLE-ARRAY BASE-CHAR NIL)
> which isn't any better, just more insidious.

This is consistent with the previous thing. (make-array nil ...) makes
a zero-dimensional array, which has one element, which you get with
(aref x). It's not a string as a string is one-dimensional. I always
get confused about this.

Tim Bradshaw

unread,
Nov 15, 2010, 6:38:46 PM11/15/10
to
On 2010-11-15 23:13:54 +0000, Andy Venikov said:

> That would make it: "NIL (the type) IS-A T (the type)", wouldn't it?

I think you mean NIL (the object) IS-A T (the type). I think what you
wrote is some kind of category error - in CL types aren't objects, and
I think if you let them be you must end up with the whole can of worms
that ate maths in the early 20th century unless you're very careful.

Robert Maas, http://tinyurl.com/uh3t

unread,
Nov 15, 2010, 7:18:58 PM11/15/10
to
> From: p...@informatimago.com (Pascal J. Bourguignon)
> > ... Any object can be used for the eof object that isn't likely to be
> > returned from the read, which includes any freshly-consed object. The
> > usual way to code this, though, is to use the stream itself; it is not
> > only not likely to be returned by read, it is much more likely to be
> > understood as an eo object (by convention) than an object that could
> > be indistinguishable from garbage in the reading context.

Hmm, not as debug-friendly to the novice user as my (copy-seq
"EOF"), but friendly-enough to experienced programmers who happen
to know the convention (as you apparently do), and less costly than
building the copy of string after already have built the new
stream. So it's a trade-off, arcane convention you must learn to
save 16 bytes, or debug-friendly even to novices at cost of extra
16 bytes. From my previous article in this thread (CMUCL):


* (time (copy-seq "EOF"))
; 16 bytes consed.

> Sure. This was just an example. You may need an object with unique
> identity in other contexts where you don't have a stream handy.

Thus my (copy-seq "EOF") or similar is the *more* general
convention, thus instead of learning one convention for streams
(use the stream itself) and another convention for each other
circumstance where you need an end-of-whatnot symbol to return, we
can (copy-seq "whatever") which is a *single* general convention to
use in *all* types of situations, costing an extra 16 bytes in some
cases where a stream or whatever is already allocted, but making it
easier for novices to catch on right away.

> My opinion is that it's interesting in a theorical point of view,
> for the completeness of the type lattice.

I agree this is a worthwhile (though arcane) discussion.

> (make-array 0 :element-type nil) is a string, but it should also be an
> element of all vector types, including bit-vector (of dimension 0).

Agreed. The return value from that form should be an empty vector
of type NIL which is sub-type of all other types, hence it *IS-A*
FOO where foo is an arbitrary type.

> It seems to me that implementations that return nil (all of them it
> seems) on (typep (make-array 0 :element-type nil) 'bit-vector) are in
> error, and while they can print (make-array 0 :element-type nil) as "",
> they could as well print it as #0* or #(), etc.

But those other print forms require more characters. "" is the
cheapest way to print it. Perhaps this is a case of the cart
dragging the horse? Maybe the thought process went like this:
[We need the most efficient print form for an empty vector of some
random type we support. Well, empty string is only 2 characters
whereas all other types require 3 or more characters, so we'll
print it *as*if* it were an empty string. Hey, if we're going to
print it as "", we might as well actually build a string in the
first place, which means we don't even need to bother implementing
the correct type here, since we've already implemented strings.]

(time (make-string 0))
; 16 bytes consed.
""

(time (make-array 0 :element-type 'base-char))
; 16 bytes consed.

(time (make-array 0 :element-type t))
; 8 bytes consed.
#()

(time (make-array 0 :element-type '(integer 0 1)))
; 8 bytes consed.
#*

(time (make-array 0 :element-type '(integer 7 42)))
; 8 bytes consed.


; Compiling LAMBDA NIL:
; In: LAMBDA NIL

; (MAKE-ARRAY 0 :ELEMENT-TYPE '(INTEGER 7 42))
; Note: Default initial element 0 is not a (INTEGER 7 42).


; Compiling Top-Level Form:
; Compilation unit finished.
; 1 note

;That's a fucking bug! I asked it for elements in range 7 to 42, so
; what fucking business does it have defaulting to 0 for an
; element, and nagging me about it when there are no elements so
; that's moot anyway?? Lazy evaluation of the default value would
; be a big win here!!
#()

(time (make-array 0 :element-type 'single-float))
; 8 bytes consed.
#()

(time (make-array 0 :element-type 'complex))


; Compiling LAMBDA NIL:
; In: LAMBDA NIL

; (MAKE-ARRAY 0 :ELEMENT-TYPE 'COMPLEX)
; Note: Default initial element 0 is not a COMPLEX.


; Compiling Top-Level Form:
; Compilation unit finished.
; 1 note

;Fucking bug again!
; 8 bytes consed.
#()
(time (make-list 0))
; 0 bytes consed.
()

Hmm, making an empty string i.e. base-char array, is twice as
expensive in CMUCL as making any of the other kinds of arrays I
tried. And type (integer 0 1) also gives only 2 characters of
print, so it seems they goofed, should have upgraded element type
NIL to (integer 0 1) instead of to base-char. Maybe they thought ""
would be more novice-friendly than #*? To tell the truth, I've been
programming in Lisp since 1973, in Common Lisp since 1990, and have
*never* before seen an empty bit array print out or appear in
source code before, so when I saw #* I was quite surprised. I can
imagine a Lisp novice doing a Google search to try to find the
meaning of that notation. I tried it just now:
Your search - #* - did not match any documents.
for comparison:
Your search - "" - did not match any documents.
I tried Google's advanced search, specifying either string as
"exact phrase", still no match with either.
Is there any search engine where either notation could be found??

Pascal J. Bourguignon

unread,
Nov 15, 2010, 7:55:35 PM11/15/10
to
Tim Bradshaw <t...@tfeb.org> writes:

> On 2010-11-15 23:00:01 +0000, Robert Maas, http://tinyurl.com/uh3t said:
>
>> In CMUCL, (make-array 0 :element-type nil) upgrades to (make-string
>> 0), so today's programmer might as well call (make-string 0)
>> instead of waiting for the standard to be improved and then wait
>> for all implementations to be conforming to the new standard.
>
> I'm not at all sure what one could do about this, though it certainly
> seems very peculiar. It certainly is a weird consequence, but it's
> not at all clear what a fix would be, at least to me. The only thing
> I can think of is simply disallowing NIL as a type-specifier to
> MAKE-ARRAY, but that sounds like a weirdo special case in turn.
> Certainly if the type graph has a bottom element (which I think is
> kind of nice as otherwise it kind of peters out, and I think may be if
> you think of types as sets of objects, you need the empty set, which
> is what this bottom is), then I think that that, combined with the
> rules for upgraded element types (which seem to me to be very hard to
> argue with), means you end up with this weird type of array.

Unless the implementation upgrades NIL to NIL, in which case there's no
problem at all. And indeed, what better, for an implementation than to
allocate a vector of zero * 0 octet (plus header), instead of trying to
upgrade it to anything and wondering whether it must allocate a vector
of zero * 1 octet or zero * 4 octets or whatever. Clearly, this will
give the same memory foot print, but conceptually, it's much cleaner to
allocate 0*0 than 0*4. IIANW, not upgrading here would give one less
special case, and therefore a simplier implementation.

> No one (that I've seen) has answered whether simply disallowing NIL as
> a type specifier in MAKE-ARRAY would be OK

It would not be satisfactorily :-)

> (and I guess in other cases
> where you are trying to specify the type of an actual thing: (defun
> foo (x) (declare (type nil x)) ...) is pretty weird. I can't work out
> whether it would be, or whether it would have some other bad
> consequence. The flip side of that is: what is the cost to
> implementations of supporting this weird array type?
>
>> But (make-array nil :element-type nil) => #0A#\Null
>> and (type-of *) => (SIMPLE-ARRAY BASE-CHAR NIL)
>> which isn't any better, just more insidious.
>
> This is consistent with the previous thing. (make-array nil ...)
> makes a zero-dimensional array, which has one element, which you get
> with (aref x). It's not a string as a string is one-dimensional. I
> always get confused about this.

(make-array nil :element-type nil) should give an error.

Since what is requested here is to allocate space for a scalar of type
NIL, but there's no such value. Upgrading nonwithstanding.

TheFlyingDutchman

unread,
Nov 15, 2010, 8:05:35 PM11/15/10
to

>
> So, NIL really is T.

It's true!

(typep nil 't) => T

and...

(typep nil 'null) => T
(typep nil 'list) => T
(typep nil 'sequence) => T

nil really is flexible!

Robert Maas, http://tinyurl.com/uh3t

unread,
Nov 15, 2010, 8:12:11 PM11/15/10
to
> > At the top, declare (defvar $memo$)
> > Just before returning T, do (setq $memo$ memo)
> From: TheFlyingDutchman <zzbba...@aol.com>

> Do $'s around a variable have a standard meaning or is that just
> your personal convention?

Oops. The convention I've ever since PSL (before CL) was G: prefix
for globals that aren't re-bound, F: prefix for fluids that don't
have a global value (these are implemented *differently* in PSL).
Then I started using CL, I found : is a special character I can't
use, so I changed it to G* and F*, with S* for specials that *both*
have a toplevel value and are rebound. This convention serves me
well, clearly documenting every time I see a variable how I
intended to use it. But for this forum, whenever I recommend using
*my* personal F* vs. G* convention, I get a lot of flak that I
should use *foo* convention instead, despite the fact it's
ambiguous as to whether the variable has a global value or is
locally bound or what. Since I never use the *foo* convention
myself, I remembered it wrong as $foo$. Sorry for confusing you for
a moment.

Maybe it would better if I insist everyone use F*foo or G*foo or
S*foo for their own special variable names, reserving *foo* for
ANSI-CL-specified special variable names such as *standard-output*.

By the way, for variables I'm using for line-at-a-time code
development in the REP, which I do *not* intend to become permanent
globals, but which *will* be globals for several days during
deveopment, I use g-foo instead of g*foo. Then when I'm enclosing
several consecutive lines of that kind of code into a function
definition, I simply drop all the "g-" prefixes but keep the "g*"
prefixes and add (defvar g*...) somewhere before the first function
that uses that variable.

Thomas A. Russ

unread,
Nov 15, 2010, 6:28:52 PM11/15/10
to
TheFlyingDutchman <zzbb...@aol.com> writes:

> >
> > Why? Ž What is the real benefit of doing this? Ž Right now you have to
> > specifically create dotted pairs or circular lists. Ž If you don't want


> > any, then don't create them!
>
> Theoretically, I could be writing code that is called by someone else.
> I would like to verify
> they have passed a proper list if that is what my function requires.

Well, in that case, I would think Pascal's solution would fit in exactly
with what you require. You can call his PROPER-LIST-P function directly
or use the type definition facility if you want to have a named type.

One of the benefit of lisp's macro system is that you can easily extend
the language in ways that you find useful or necessary and have the
extensions fit in seamlessly with the regular lisp syntax. And the type
system also allows TYPECASE dispatch on these user-defined types.

It is loading more messages.
0 new messages