From: verec
Subject: defstruct, BOA &key parameter ...
Date: 
Message-ID: <4387a1b4$0$38043$5a6aecb4@news.aaisp.net.uk>
Let's assume I have:



(defstruct words
  (store (make-array 0 :element-type character :adjustable t))
  (count 0 :type integer))



I've already written all the code that operates on words,

and now I see that I need the exact same code a second time,

but for index instead.



(defstruct index
  (store (make-array 0 :element-type integer :adjustable t))
  (count 0 :type integer))



Since I'm not that much into copy-and-paste :-) I'd rather

have the code written once, and parameterized on the array

type at construction time ...



ideally, I would want to be able to use such new code with:



(setf *words* (make-dynarray :type character)

(setf *index* (make-dynarray :type integer)



So here's a constructor less version.



(deftsruct dynarray

    (store  nil)

    (count  0   :type integer))



But now of course I need to create a special constructor

that will take a type argument and create the appropriate

kind of array,



And that's where CLTL2/CLHS confuse me no end :-(



I was willing to use the "BOA constructor" idiom, but

the syntax confuses me (And yes I'd like to not forego

the documentation string either :)



(defstruct (dynarray

    "Is this where the doc string goes?"

            (:constructor (&key type)

                (store Huh? how do I assign store with 

                    (make-array 0 :element-type type :adjustable t)

                    here?

                (count 0)))

            (store count))

Many Thanks.
-- 
JFB  ()

From: jayessay
Subject: Re: defstruct, BOA &key parameter ...
Date: 
Message-ID: <m3k6ewme4u.fsf@rigel.goldenthreadtech.com>
verec <·····@mac.com> writes:


> (setf *words* (make-dynarray :type character)
> 
> (setf *index* (make-dynarray :type integer))
> 
> 
> 
> So here's a constructor less version.
> 
> 
> 
> (deftsruct dynarray
> 
>     (store  nil)
> 
>     (count  0   :type integer))

Is this a typo or a new macro of yours "deftstruct"?  If simply a typo
of defstruct, then this will generate a constructor make-dynarray.

I'm not sure what you are getting at but this may be what you want:

(defstruct (dynarray (:constructor base-make-dynarray (store count)))
  "This where the doc string goes"
  (store nil)
  (count 0))

(defun make-dynarray (&key type)
  (base-make-dynarray (make-array 0 :element-type type :adjustable t) 0))

(setf *words* (make-dynarray :type 'character)
=> #S(DYNARRAY :STORE "" :COUNT 0)

(setf *index* (make-dynarray :type 'integer))
=> #S(DYNARRAY :STORE #() :COUNT 0)

If you don't like the quote on the type, change make-dynarray to a
macro.  If you want to pass make-dynarray as an argument to funcall,
such as to a map function, then make it a compiler macro.



/Jon

-- 
'j' - a n t h o n y at romeo/charley/november com
From: verec
Subject: Re: defstruct, BOA &key parameter ...
Date: 
Message-ID: <4387ff74$0$38040$5a6aecb4@news.aaisp.net.uk>
On 2005-11-26 00:44:33 +0000, jayessay <······@foo.com> said:

> verec <·····@mac.com> writes:
> 
>> (setf *words* (make-dynarray :type character)
>> (setf *index* (make-dynarray :type integer))
>> 
>> So here's a constructor less version.
>> 
>> (deftsruct dynarray
>>   (store  nil)
>>   (count  0   :type integer))
> 
> Is this a typo or a new macro of yours "deftstruct"?  If simply a typo
> of defstruct, then this will generate a constructor make-dynarray.

Sorry about that. Yes, if was a typo.

> I'm not sure what you are getting at but this may be what you want:
> 
> (defstruct (dynarray (:constructor base-make-dynarray (store count)))
>   "This where the doc string goes"
>   (store nil)
>   (count 0))
> 
> (defun make-dynarray (&key type)
>   (base-make-dynarray (make-array 0 :element-type type :adjustable t) 0))

I had to read it a couple of times (OK, I'm lying: 3 or 4 :-) until
I got it. But you're absolutely right.

I was puzzled by the ice-cream-factory example in CLTL2 page 484
which lost on the path of defining the whole thing within the
defstruct scope. Your solution sounds like a good example of
lateral thinking! Many thanks.


> (setf *words* (make-dynarray :type 'character)
> => #S(DYNARRAY :STORE "" :COUNT 0)
> 
> (setf *index* (make-dynarray :type 'integer))
> => #S(DYNARRAY :STORE #() :COUNT 0)
> 
> If you don't like the quote on the type,

I'm not at the stage (yet) where I can express what I like or
dislike about Lisp. For me, everything is idiomatic, quote
included, until I know better :-)

> change make-dynarray to a
> macro.  If you want to pass make-dynarray as an argument to funcall,
> such as to a map function, then make it a compiler macro.

I'll keep a note.

Thanks again.
-- 
JFB  ()
From: Pascal Bourguignon
Subject: Re: defstruct, BOA &key parameter ...
Date: 
Message-ID: <87d5kow82k.fsf@thalassa.informatimago.com>
verec <·····@mac.com> writes:

> Let's assume I have:
>
>
>
> (defstruct words
>   (store (make-array 0 :element-type character :adjustable t))
                                       (quote character)
unless you wrote:
(shadow 'character)
(defparameter character 'cl:character)

>   (count 0 :type integer))
>
>
>
> I've already written all the code that operates on words,
>
> and now I see that I need the exact same code a second time,
>
> but for index instead.
>
>
>
> (defstruct index
>   (store (make-array 0 :element-type integer :adjustable t))
                                       (quote integer)
unless you wrote:
(shadow 'integer)
(defparameter integer 'cl:integer)

>   (count 0 :type integer))
>
>
>
> Since I'm not that much into copy-and-paste :-) I'd rather
>
> have the code written once, and parameterized on the array
>
> type at construction time ...
>
>
>
> ideally, I would want to be able to use such new code with:
>
>
>
> (setf *words* (make-dynarray :type character)
>
> (setf *index* (make-dynarray :type integer)

Which would give an error unless you bound  the character and integer
variables as I did above.

But I'd just write:

(defparameter *word-and-index*
    (mapcar (lambda (type) (make-dynarray :type type))
            '(character integer))) ; without the shadows!


> So here's a constructor less version.
>
>
>
> (deftsruct dynarray
>
>     (store  nil)
>
>     (count  0   :type integer))
>
>
>
> But now of course I need to create a special constructor
>
> that will take a type argument and create the appropriate
>
> kind of array,
>
>
>
> And that's where CLTL2/CLHS confuse me no end :-(
>
>
>
> I was willing to use the "BOA constructor" idiom, but
>
> the syntax confuses me (And yes I'd like to not forego
>
> the documentation string either :)
>
>
>
> (defstruct (dynarray
>
>     "Is this where the doc string goes?"
>
>             (:constructor (&key type)
>
>                 (store Huh? how do I assign store with 
>
>                     (make-array 0 :element-type type :adjustable t)
>
>                     here?
>
>                 (count 0)))
>
>             (store count))
>
> Many Thanks.

Why should dynarray be a structure?  

I'd infer that the count slot stores the number of elements you've
stored in the array.  Why don't you use the fill pointer then?


(defun make-dynarray (&key (type t))
   (make-array 0 :adjustable t :fill-pointer 0
               :element-type    (typecase type
                                  ((or character number string) type)
                                  (otherwise `(or null ,type)))
               :initial-element (typecase type
                                   (character #\space)
                                   (number    0)
                                   (string    "")
                                   (otherwise nil))))


(defparameter *word* (make-dynarray :type (quote character)))
(vector-push-extend #\H *word*)
(vector-push-extend #\e *word*)
(vector-push-extend #\l *word*)
(vector-push-extend #\l *word*)
(vector-push-extend #\o *word*)

(defparameter *words* (make-dynarray :type (quote string)))
(vector-push-extend "Hello" *words*)
(vector-push-extend "world" *words*)

(defparameter *numbers* (make-dynarray :type (quote number)))
(vector-push-extend 0 *numbers*)
(vector-push-extend -1 *numbers*)
(vector-push-extend pi *numbers*)
(vector-push-extend (exp 1) *numbers*)

(defparameter *stuff* (make-dynarray :type (quote (or symbol string))))
(vector-push-extend 'hello  *stuff*)
(vector-push-extend "world" *stuff*)

(values  *word* *words* *numbers* *stuff*)
-->
#(#\H #\e #\l #\l #\o) ;
#("Hello" "world") ;
#(0 -1 3.1415926535897932385L0 2.7182817) ;
#(HELLO "world")


[87]> (mapcar (function length) (list  *word* *words* *numbers* *stuff*))
(5 2 4 2)
[88]> (mapcar (function array-dimensions) (list  *word* *words* *numbers* *stuff*))
((16) (16) (16) (16))
[89]> (mapcar (lambda (a) (aref a (1- (first (array-dimensions a))))) (list  *word* *words* *numbers* *stuff*))
(NIL NIL NIL NIL) ; !!

Note, in sbcl, we get 0 instead of NIL:

* (mapcar (function array-dimensions) (list  *word* *words* *numbers* *stuff*))

((7) (3) (7) (3))
* (mapcar (lambda (a) (aref a (1- (first (array-dimensions a))))) (list  *word* *words* *numbers* *stuff*))

(0 0 0 0) ; !!


Of course, I would have expected the :initial-element be used to
extend the vectors, but it should not matter since we're only
interested in the elements below the fill-pointer.


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
The mighty hunter
Returns with gifts of plump birds,
Your foot just squashed one.
From: verec
Subject: Re: defstruct, BOA &key parameter ...
Date: 
Message-ID: <438804b5$0$38045$5a6aecb4@news.aaisp.net.uk>
On 2005-11-26 00:45:23 +0000, Pascal Bourguignon <····@mouse-potato.com> said:

> verec <·····@mac.com> writes:
>> Let's assume I have:
>> 
>> (defstruct words
>> (store (make-array 0 :element-type character :adjustable t))
>                                        (quote character)
> unless you wrote:
> (shadow 'character)
> (defparameter character 'cl:character)

Sorry. The missing quote was (mildly) puzzling me, but I'm not sure why 
I left it
out though.

> But I'd just write:
> 
> (defparameter *word-and-index*
>     (mapcar (lambda (type) (make-dynarray :type type))
>             '(character integer))) ; without the shadows!

That's nice except that it binds the knowledge about all possible
element types in one place, whereas

> (setf *words* (make-dynarray :type 'character))

produces them on demand.

> Why should dynarray be a structure?

Because I want a single handle to two (for now) bits of
information, the actual array and the real count, irrespective
of the array size.

> I'd infer that the count slot stores the number of elements you've
> stored in the array.  Why don't you use the fill pointer then?

My limited understanding of the fill-pointer didn't seem to fit
my requirement.

The store slot of the dynarray (the array part) is updated each
time count would exceed the array capacity. That's why I called
the whole thing dynamic-array, after all.:)

> (defun make-dynarray (&key (type t))
>    (make-array 0 :adjustable t :fill-pointer 0
>                :element-type    (typecase type
>                                   ((or character number string) type)
>                                   (otherwise `(or null ,type)))
>                :initial-element (typecase type
>                                    (character #\space)
>                                    (number    0)
>                                    (string    "")
>                                    (otherwise nil))))

That's interesting, but in addition to the fact that I don't
understand the correlation between your use of the fill-pointer
and my need to dynamically resize the array, this example, again,
binds all the type knowledge in one place that has to be modified
should I want to extend the supported set of types.

> (defparameter *word* (make-dynarray :type (quote character)))
> (vector-push-extend #\H *word*)
[...]
> [87]> (mapcar (function length) (list  *word* *words* *numbers* *stuff*))
> (5 2 4 2)
[...]
> * (mapcar (function array-dimensions) (list  *word* *words* *numbers* *stuff*))
> ((7) (3) (7) (3))

Obviously, your solution integrates extremely well in CL, but is probably
beyond my reach at the moment. I mean that I'm not in depth-first mode
while studying Lisp, but in breadth-first mode :-)

Many thanks.
-- 
JFB  ()
From: Pascal Bourguignon
Subject: Re: defstruct, BOA &key parameter ...
Date: 
Message-ID: <87r793vmgu.fsf@thalassa.informatimago.com>
verec <·····@mac.com> writes:
>> But I'd just write:
>> (defparameter *word-and-index*
>>     (mapcar (lambda (type) (make-dynarray :type type))
>>             '(character integer))) ; without the shadows!
>
> That's nice except that it binds the knowledge about all possible
> element types in one place, whereas
>
>> (setf *words* (make-dynarray :type 'character))
>
> produces them on demand.

No, you missed the point: (make-dynarray :type type)
you can use a variable type.


>> Why should dynarray be a structure?
>
> Because I want a single handle to two (for now) bits of
> information, the actual array and the real count, irrespective
> of the array size.

What is the "real count"?


>> I'd infer that the count slot stores the number of elements you've
>> stored in the array.  Why don't you use the fill pointer then?
>
> My limited understanding of the fill-pointer didn't seem to fit
> my requirement.
>
> The store slot of the dynarray (the array part) is updated each
> time count would exceed the array capacity. That's why I called
> the whole thing dynamic-array, after all.:)
>
>> (defun make-dynarray (&key (type t))
>>    (make-array 0 :adjustable t :fill-pointer 0
>>                :element-type    (typecase type
>>                                   ((or character number string) type)
>>                                   (otherwise `(or null ,type)))
>>                :initial-element (typecase type
>>                                    (character #\space)
>>                                    (number    0)
>>                                    (string    "")
>>                                    (otherwise nil))))
>
> That's interesting, but in addition to the fact that I don't
> understand the correlation between your use of the fill-pointer
> and my need to dynamically resize the array, this example, again,
> binds all the type knowledge in one place that has to be modified
> should I want to extend the supported set of types.

Not exactly.  Again, watch how the type is a variable.  

You must specify an explicit :initial-element because the default one
is NIL and if you specify an :element-type, the :initial-element,
default or not, must be compatible with the :element-type, to produce
a conforming program.  If you don't do it, you fall into
implementation specific behavior.

You can still call make-dynarray with any random type:


[122]> (make-dynarray :type (print (eval `(defstruct ,(gentemp) 
                             ,@(loop :repeat (random 10) :collect (gentemp))))))

T6 
#()
[123]> (vector-push-extend (make-t6) *)
0
[124]> **
#(#S(T6 :T7 NIL :T8 NIL :T9 NIL :T10 NIL :T11 NIL :T12 NIL :T13 NIL :T14 NIL
     :T15 NIL))
[125]> 


>> (defparameter *word* (make-dynarray :type (quote character)))
>> (vector-push-extend #\H *word*)
> [...]
>> [87]> (mapcar (function length) (list  *word* *words* *numbers* *stuff*))
>> (5 2 4 2)
> [...]
>> * (mapcar (function array-dimensions) (list  *word* *words* *numbers* *stuff*))
>> ((7) (3) (7) (3))
>
> Obviously, your solution integrates extremely well in CL, but is probably
> beyond my reach at the moment. I mean that I'm not in depth-first mode
> while studying Lisp, but in breadth-first mode :-)

Then browse more broadly: have a look at the Chapter 15 Arrays of CLHS.

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

Nobody can fix the economy.  Nobody can be trusted with their finger
on the button.  Nobody's perfect.  VOTE FOR NOBODY.
From: verec
Subject: Re: defstruct, BOA &key parameter ...
Date: 
Message-ID: <43888e29$0$38041$5a6aecb4@news.aaisp.net.uk>
On 2005-11-26 08:32:01 +0000, Pascal Bourguignon <····@mouse-potato.com> said:

> verec <·····@mac.com> writes:
>>> But I'd just write:
>>> (defparameter *word-and-index*
>>> (mapcar (lambda (type) (make-dynarray :type type))
>>> '(character integer))) ; without the shadows!
>> 
>> That's nice except that it binds the knowledge about all possible
>> element types in one place, whereas
>> 
>>> (setf *words* (make-dynarray :type 'character))
>> 
>> produces them on demand.
> 
> No, you missed the point: (make-dynarray :type type)
> you can use a variable type.

You're right.

>>> Why should dynarray be a structure?
>> 
>> Because I want a single handle to two (for now) bits of
>> information, the actual array and the real count, irrespective
>> of the array size.
> 
> What is the "real count"?

The actual number of elements in the array as opposed to
its capacity, which can be, in my implementation, up to
3/2 times as big as required (until trimmed down).

(defun capacity (a)
	(length (dynarray-store a))

and

(defun actual-elemment-count (a)
	(dynarray-count a))

such that it is always the case that

(defun invariant (a)
	(>= (capacity a) (actual-elemment-count a)))

returns T.

This is so that I can grow the array by sizeable chunks rather
than doing it every single time I have to append an item to it.

Hence the need for a final trim operation when all the appending
is done, so as to "reclaim" the excess storage.

> You must specify an explicit :initial-element because the default one
> is NIL and if you specify an :element-type, the :initial-element,
> default or not, must be compatible with the :element-type, to produce
> a conforming program.  If you don't do it, you fall into
> implementation specific behavior.

I'm sure you're right, but I'm a bit surprised. Are you saying that

(defstruct dynarray
  (store nil)
  (count 0 :type 'integer))

(defun test ()
  (let ((a make-dynarray))
    (setf (a-store a) (make-array 0 :element-type 'integer :adjustable t))
    (setf (a-store a) (make-array 0 :element-type 'character :adjustable t))))

Whether the second setf actually works is implementation dependant
because the first would have set the type of the store slot to be
incompatible with the type the second would make it?

If so, that's fine with me, since, once set, I don't intend to switch
the type of the contained elements.

OTOH if you're saying that changing the value of "store" from nil
to *anything* (including some array) is a type violation then whatever
little Latin I still knew is forever lost :-(

I would understand the above argument if stated for any "statically
typed" language. If it turns out to be true of CL, then suddenly, I
have to ask myself whether my time is well spent here, and whether
I shouldn't turn to Io (www.iolanguage.com) instead.		
	
>>> (defparameter *word* (make-dynarray :type (quote character)))
>>> (vector-push-extend #\H *word*)
>> [...]
>>> [87]> (mapcar (function length) (list  *word* *words* *numbers* *stuff*))
>>> (5 2 4 2)
>> [...]
>>> * (mapcar (function array-dimensions) (list  *word* *words* *numbers* *stuff*))
>>> ((7) (3) (7) (3))
>> 
>> Obviously, your solution integrates extremely well in CL, but is probably
>> beyond my reach at the moment. I mean that I'm not in depth-first mode
>> while studying Lisp, but in breadth-first mode :-)
> 
> Then browse more broadly: have a look at the Chapter 15 Arrays of CLHS.

There is a saying:

"Make the simple case easy, and the difficult one, possible"

As I said (many times) earlier, I'm not the kind of guy who will
spend an entire year (or more!) understanding every single nuance
of every 29 chapters of CLTL2, and *then* start writing code.

I am convinced that I will progress much faster by trial and error,
until I get stuck. At which point I turn to CLTL2 (or the CLHS, only
to be disappointed most of the time, as, again, already mentioned).

It is the case, that in this specific example, I grasped enough of
the CL array behavior to let me going with the first version which
had a hard-code 'character type to its array.

Now, I want to do a *simple* thing: have the array type be soft-coded,
and I find my self directed to fill-pointers and the like, I just don't
need to know anything about *right now*.

As Abelson put it in his SICP lecture, that's stuff for "George".

When I start needing to see the array black box as a greyish, or
even worse, whitish box, I really will have no choice but put
on George's hat. But not *now*

Pascal, no criticism intended, but you should probably contrast
your kind of answers to that of, for example, JayEssay, in this
very thread.

His answer was helpful, to the point, and concise...

From my side, the "Lisp barrier to entry" is very high. Most of the
things I take for granted (in, gasp, Java) I have to unlearn, relearn
differently.

In the same 4 week-ends that I needed to write the full Java version
of my crossword example, with GUI and threads included, I've only
managed, so far, to make the appending part of dictionary in the Lisp
version.

This is a price I'm willing to pay, because I *sense* a greater
potential when I reach the same level of expertise as in Java.

Don't discourage me by pointing at hesoteric stuff I'm sure has
its place, but not that early.

(small print: never answer a question that is not asked unless there
 is a strong rationale to do so)

Many thanks.
-- 
JFB  ()
From: jayessay
Subject: Re: defstruct, BOA &key parameter ...
Date: 
Message-ID: <m3fypjmfua.fsf@rigel.goldenthreadtech.com>
verec <·····@mac.com> writes:

> On 2005-11-26 08:32:01 +0000, Pascal Bourguignon <····@mouse-potato.com>said:
> 
> > You must specify an explicit :initial-element because the default one
> > is NIL and if you specify an :element-type, the :initial-element,
> > default or not, must be compatible with the :element-type, to produce
> > a conforming program.  If you don't do it, you fall into
> > implementation specific behavior.
> 
> I'm sure you're right, but I'm a bit surprised. Are you saying that
> 
> (defstruct dynarray
>   (store nil)
>   (count 0 :type 'integer))
> 
> (defun test ()
>   (let ((a make-dynarray))
>     (setf (a-store a) (make-array 0 :element-type 'integer :adjustable t))
>     (setf (a-store a) (make-array 0 :element-type 'character :adjustable t)))
> 
> Whether the second setf actually works is implementation dependant
> because the first would have set the type of the store slot to be
> incompatible with the type the second would make it?

I think you simply misread what Pascal said.  He's talking about
ensuring that the elements in the initial contents of the array are of
a compatible type to the declared element type given by :element-type.
If you don't specify an :initial-element, it defaults to nil.  If you
declared the element type :element-type 'character, _and_ the array
actually _had_ an initial content (initial size > 0), the nil elements
given in the absence of a :initial-element #\Space or some such, would
yield "undefined consequences" (basically implementation dependent
behavior) upon accessing/processing.  You could also use
:initial-content, if the elements are not all the same value.

In your specific case, you appear to always be creating the array with
an initial size of 0, so there is no content and so providing
:initial-element or :intitial-content has no effect anyway.  If you
later decided to create at least some with an initial size > 0, then
you would be well advised to supply the :initial-(element|content)
option.

> Pascal, no criticism intended, but you should probably contrast
> your kind of answers to that of, for example, JayEssay, in this
> very thread.

Pascal's (both of them!, though IMO, Costanza's are typically
exceptionally good) answers are much more detailed and provide more
complete coverage than what I typically give - I just don't have the
time to provide that level of answer.  So, while mine may "do the job"
or are "good enough to get past this bump", it is probably a good idea
to try to understand the more complete ones as well.


> This is a price I'm willing to pay, because I *sense* a greater
> potential when I reach the same level of expertise as in Java.

Your "sense" here is quite accurate, make no mistake on that.  The
potential isn't just greater, it is _vastly_ greater than Java (or
most other "candidates" for that matter).


/Jon

-- 
'j' - a n t h o n y at romeo/charley/november com
From: verec
Subject: Re: defstruct, BOA &key parameter ...
Date: 
Message-ID: <4388b4ce$0$38046$5a6aecb4@news.aaisp.net.uk>
On 2005-11-26 18:19:57 +0000, jayessay <······@foo.com> said:

> verec <·····@mac.com> writes:
> 
>> On 2005-11-26 08:32:01 +0000, Pascal Bourguignon <····@mouse-potato.com>said:
>> 
>>> You must specify an explicit :initial-element because the default one
>>> is NIL and if you specify an :element-type, the :initial-element,
>>> default or not, must be compatible with the :element-type, to produce
>>> a conforming program.  If you don't do it, you fall into
>>> implementation specific behavior.
>> 
>> I'm sure you're right, but I'm a bit surprised. Are you saying that
>> 
>> (defstruct dynarray
>> (store nil)
>> (count 0 :type 'integer))
>> 
>> (defun test ()
>> (let ((a make-dynarray))
>> (setf (a-store a) (make-array 0 :element-type 'integer :adjustable t))
>> (setf (a-store a) (make-array 0 :element-type 'character :adjustable t)))
>> 
>> Whether the second setf actually works is implementation dependant
>> because the first would have set the type of the store slot to be
>> incompatible with the type the second would make it?
> 
> I think you simply misread what Pascal said.

Surely so ...

>  He's talking about
> ensuring that the elements in the initial contents of the array are of
> a compatible type to the declared element type given by :element-type.

I'm not saying that the above is wrong, simply that it is off-topic, unless

(defvar *dynarray* nil)
(setf *dynarray* (make-array 0 :element-type 'integer :adjustable t))
(setf *dynarray* (make-array 0 :element-type 'character :adjustable t))

is undefined behavior, that is.

It is nowhere the case that I intended to have a initial contents of
type foo only to "override" it with contents of type bar. In any case,
in all this discussion a brand new array is created afresh, the result
of which is assigned to some slot.

Since the thread title (and my initial problem) is about how to
pass an argument to a struct constructor such that the passed in
argument (the type of the array element) does NOT have a corresponding
slot, but is USED in the making of the said slot, I don't see how
one could infer anything about array element type conflicts.

This is the _constructor_ of a struct, which, again, unless I have
completely misunderstood what defstruct is supposed to do, is not
supposed to do anything but _initialise_ slots, so it is not the
case that the slot "storage" could contain anything of interest,
but even if it did, the whole purpose is to overwrite that slot
with a freshly allocated array. So the former contents, whatever
type it might have been, just cannot conflict with the new contents.
The old contents is just food for the GC.

Now I take your point that if I was trying to make-array _displaced_
to some other array, then I'd better make sure that both array
have compatible types. But this is not the case.

> If you don't specify an :initial-element, it defaults to nil.  If you
> declared the element type :element-type 'character, _and_ the array
> actually _had_ an initial content (initial size > 0),

Sorry, sorry, sorry. Which array are we talking about?

Are you saying that

(defstruct a
	(store (make-array 1 :element-type 'integer :adjustable t)))

followed by

(defun reset (a)
	(setf (a-store a) (make-array 1 :element-type 'charcter :adjustable t)))

results in undefined behavior?

Why should the previous contents of the store slot be of any significance?
I'm _overwriting_ it, or so I beleive ...

> the nil elements
> given in the absence of a :initial-element #\Space or some such, would
> yield "undefined consequences" (basically implementation dependent
> behavior) upon accessing/processing.  You could also use
> :initial-content, if the elements are not all the same value.
> 
> In your specific case, you appear to always be creating the array with
> an initial size of 0, so there is no content and so providing
> :initial-element or :intitial-content has no effect anyway.  If you
> later decided to create at least some with an initial size > 0, then
> you would be well advised to supply the :initial-(element|content)
> option.

Again, I don't see how this applies. I'm must be extremely dense today :-(

>> This is a price I'm willing to pay, because I *sense* a greater
>> potential when I reach the same level of expertise as in Java.
> 
> Your "sense" here is quite accurate, make no mistake on that.  The
> potential isn't just greater, it is _vastly_ greater than Java (or
> most other "candidates" for that matter).

I'm verifying this assertion by myself every week-end :-)

Many thanks for your time.
-- 
JFB  ()
From: jayessay
Subject: Re: defstruct, BOA &key parameter ...
Date: 
Message-ID: <m3br07m7sm.fsf@rigel.goldenthreadtech.com>
verec <·····@mac.com> writes:

> On 2005-11-26 18:19:57 +0000, jayessay <······@foo.com> said:
> 
> > verec <·····@mac.com> writes:
...
> >  He's talking about
> > ensuring that the elements in the initial contents of the array are of
> > a compatible type to the declared element type given by :element-type.
> 
> I'm not saying that the above is wrong, simply that it is off-topic, unless
> 
> (defvar *dynarray* nil)
> (setf *dynarray* (make-array 0 :element-type 'integer :adjustable t))
> (setf *dynarray* (make-array 0 :element-type 'character :adjustable t))
> 
> is undefined behavior, that is.

No, that's fine, but it is irrelevant to the point (attempting) to be
made.  Which is that if you create an array with size > 0 and thus the
_initial_ array has content, then you would be wise to ensure that
that content is of a type compatible with the :element-type you
specify the array to be.  Otherwise, you _can_ run afoul of "undefined
consequences".  That's all.  Nothing more.


> It is nowhere the case that I intended to have a initial contents of
> type foo only to "override" it with contents of type bar.

Again, that isn't the issue.  The issue is about the content at the
point of creation.


> slot, but is USED in the making of the said slot, I don't see how
> one could infer anything about array element type conflicts.

Because _if_ you create the array with content (size > 0) and you
don't specify the initial content, it defaults to nil, which may well
be incompatible with the specified element type (for example, either
'integer or 'character).

Now, as I said, in _your specific case_ you don't _appear_ to be
creating any of these arrays with any content, i.e., they are _always_
created with initial size of 0 => no initial content => no possible
initial element conflict with the specified element type.


> Now I take your point that if I was trying to make-array _displaced_
> to some other array, then I'd better make sure that both array
> have compatible types. But this is not the case.

You're trying too hard.  Here's an example of the issue:

(make-array 10 :element-type 'character :adjustable t)

Since the initial-(element|content) isn't specified, the elements are
basically implementation defined.  Use of this array in a string
operation _might_ blow up because the elements provided might not be
legal characters.


> > If you don't specify an :initial-element, it defaults to nil.  If you
> > declared the element type :element-type 'character, _and_ the array
> > actually _had_ an initial content (initial size > 0),
> 
> Sorry, sorry, sorry. Which array are we talking about?

The one for field STORE of your dynarray structure.


> Are you saying that
> 
> (defstruct a
>   (store (make-array 1 :element-type 'integer :adjustable t)))
> 
> followed by
> 
> (defun reset (a)
>   (setf (a-store a) (make-array 1 :element-type 'charcter :adjustable t)))
> 
> results in undefined behavior?

Forget about the "followed by" bit.  In this case the very first
make-array _can_ actually result in "undefined consequences" since it
_has_ a size > 0 (namely 1) => it has content, but you haven't given
an :initial-(element|content) => an _implementation defined_ value of
the element in this one slot.

The subsequent setting of (as-store a) has nothing to do with it.  You
could even set it to an integer value and that would be fine.


> > In your specific case, you appear to always be creating the array
> > with
> > an initial size of 0, so there is no content and so providing
> > :initial-element or :intitial-content has no effect anyway.  If you
> > later decided to create at least some with an initial size > 0, then
> > you would be well advised to supply the :initial-(element|content)
> > option.
> 
> Again, I don't see how this applies. I'm must be extremely dense today :-(

This was the bit you needed to get clear on.  Maybe the above clears
it up.


/Jon

-- 
'j' - a n t h o n y at romeo/charley/november com
From: verec
Subject: Re: defstruct, BOA &key parameter ...
Date: 
Message-ID: <4388d504$0$38039$5a6aecb4@news.aaisp.net.uk>
On 2005-11-26 21:13:45 +0000, jayessay <······@foo.com> said:

>> (defstruct a
>> (store (make-array 1 :element-type 'integer :adjustable t)))
>> 
>> followed by
>> 
>> (defun reset (a)
>> (setf (a-store a) (make-array 1 :element-type 'charcter :adjustable t)))
>> 
>> results in undefined behavior?
> 
> Forget about the "followed by" bit.  In this case the very first
> make-array _can_ actually result in "undefined consequences" since it
> _has_ a size > 0 (namely 1) => it has content, but you haven't given
> an :initial-(element|content) => an _implementation defined_ value of
> the element in this one slot.

OK. Finally, I'm getting your point. Either the constructor gives
an init size of 0 for the array (as in my case) and everything is
fine, whatever initial contents might be, or the constructor does
specify an init size greater than zero, in which case I must have
an init-form that supplies an init-value that is consitent with
the type of elements the constructor is creating more than zero of.


I'm sorry I took so long to get it. It's very cold here in the UK
and my brain must have been freezing :-(

Many thanks for your time and patience.
-- 
JFB  ()
From: jayessay
Subject: Re: defstruct, BOA &key parameter ...
Date: 
Message-ID: <m37jaun6om.fsf@rigel.goldenthreadtech.com>
verec <·····@mac.com> writes:

> On 2005-11-26 21:13:45 +0000, jayessay <······@foo.com> said:
> 
> >> (defstruct a
> >> (store (make-array 1 :element-type 'integer :adjustable t)))
> >> followed by
> >> (defun reset (a)
> >> (setf (a-store a) (make-array 1 :element-type 'charcter :adjustable t)))
> >> results in undefined behavior?
> > Forget about the "followed by" bit.  In this case the very first
> > make-array _can_ actually result in "undefined consequences" since it
> > _has_ a size > 0 (namely 1) => it has content, but you haven't given
> > an :initial-(element|content) => an _implementation defined_ value of
> > the element in this one slot.
> 
> OK. Finally, I'm getting your point. Either the constructor gives
> an init size of 0 for the array (as in my case) and everything is
> fine, whatever initial contents might be

Things are fine in this case because there _is no_ content, ergo there
can't be any bogus content.  An array of size 0 has no elements.

I haven't paid much attention to what you are actually trying to do,
but now I notice that you are probably trying to reinvent what
adjustable arrays.  Take another look at the CLHS (or CLTL2) on
adjustable arrays and I bet they already are a superset of your
dynarrays.  In particular

(defconstant +my-starting-size+ 11)

(make-array
  +my-starting-size+
  :element-type 'integer
  :adjustable t
  :fill-pointer 0)

allocates an array of starting size 11, but which is currently empty
(fill-pointer of 0).  You can add to this array using vector-push up
until you hit the _current_ allocated size.  If you use
vector-push-extend, since the array has been specified as
_adjustable_, you can add beyond the current allocation.
vector-push-extend will "adjust" the allocated size to something
bigger than the current size and then place the new element into the
result.  How much bigger, can be influenced by you, joe programmer, by
means of the optional extension parameter of vector-push-extend.
Trivial example:

(setf *jsa*
      (make-array 10 :fill-pointer 0 :element-type 'integer :adjustable t))
==> #()

(inspect *jsa*)
==> (ARRAY T (10)) header @ #x71415992 = #()
   0 excl-type ----> Bit field: #x40
   1 flags --------> Bit field: #x03
   2 rank ---------> Bit field: #x0001
   3 fill-pointer -> fixnum 0 [#x00000000]
   4 data ---------> (NIL . #(NIL NIL NIL...)), a dotted list with 1 element
   ...

NOTE the nils in the data!!  But this is ok, since the array is in
fact empty (fill point of 0):

(length *jsa*)
==> 0

(vector-push 1 *jsa*)
==> 0 ; last value of fill pointer
(length *jsa*)
==> 1

(dotimes (i 11) (vector-push-extend i *jsa*))
==> nil ; result of the dotimes

(length *jsa*)
==> 12 ; note that the array is now bigger than originally allocated!

(inspect *jsa)
==>(ARRAY T (30)) header @ #x71418522 = #(1 0 1 2 3 4 5 6 7 8 9 10)
   0 excl-type ----> Bit field: #x40
   1 flags --------> Bit field: #x03
   2 rank ---------> Bit field: #x0001
   3 fill-pointer -> fixnum 12 [#x00000030]
   4 data ---------> (NIL . #(1 0 1...)), a dotted list with 1 element

NOTE: the allocated size of the array is now 30.


> , or the constructor does specify an init size greater than zero, in
> which case I must have an init-form that supplies an init-value that
> is consitent with the type of elements the constructor is creating
> more than zero of.

That's basically it.  Note this whole thing is really just a simple
variant of the old "uninitialized variable" issue.


/Jon

-- 
'j' - a n t h o n y at romeo/charley/november com
From: verec
Subject: Re: defstruct, BOA &key parameter ...
Date: 
Message-ID: <438963cc$0$38040$5a6aecb4@news.aaisp.net.uk>
On 2005-11-27 02:52:25 +0000, jayessay <······@foo.com> said:

> I haven't paid much attention to what you are actually trying to do,
> but now I notice that you are probably trying to reinvent what
> adjustable arrays.  Take another look at the CLHS (or CLTL2) on
> adjustable arrays and I bet they already are a superset of your
> dynarrays.

Yes. Pascal Bourguigon finally convinced me yesterday that I had
been too dumb, and was, indeed, reinventing the wheel. With the
insight that both of you just gave me, the array part of
CLTL2 suddenly has a new meaning. For some reason, I had been
blinded by the "displaced" array feature, and thought that it
was very cute, but not that useful in everyday situation, and
completely missed: the fill pointer, that arrays of dimension 1
were also called vectors, and thus completely skimmed over
vector-push & vector-push-extend ...

As a punishment, I just ordered Graham's ANSI CL, Norvig's
PAI and Siebel's PCL from Amazon, to complement my aging
Winston and Horn & CLTL2 :-)

Many thanks to both of you.
-- 
JFB  ()
From: Peter Seibel
Subject: Re: defstruct, BOA &key parameter ...
Date: 
Message-ID: <m2veyddg3e.fsf@gigamonkeys.com>
verec <·····@mac.com> writes:

> On 2005-11-27 02:52:25 +0000, jayessay <······@foo.com> said:
>
>> I haven't paid much attention to what you are actually trying to do,
>> but now I notice that you are probably trying to reinvent what
>> adjustable arrays.  Take another look at the CLHS (or CLTL2) on
>> adjustable arrays and I bet they already are a superset of your
>> dynarrays.
>
> Yes. Pascal Bourguigon finally convinced me yesterday that I had
> been too dumb, and was, indeed, reinventing the wheel. With the
> insight that both of you just gave me, the array part of
> CLTL2 suddenly has a new meaning. For some reason, I had been
> blinded by the "displaced" array feature, and thought that it
> was very cute, but not that useful in everyday situation, and
> completely missed: the fill pointer, that arrays of dimension 1
> were also called vectors, and thus completely skimmed over
> vector-push & vector-push-extend ...
>
> As a punishment, I just ordered Graham's ANSI CL, Norvig's
> PAI and Siebel's PCL from Amazon, to complement my aging
          ^^^^^^

Man, I'd probably get a lot more Google juice if my name wasn't so
easy to misspell. ;-)

-Peter

-- 
Peter Seibel           * ·····@gigamonkeys.com
Gigamonkeys Consulting * http://www.gigamonkeys.com/
Practical Common Lisp  * http://www.gigamonkeys.com/book/
From: verec
Subject: Re: defstruct, BOA &key parameter ...
Date: 
Message-ID: <438b8d84$0$20532$5a6aecb4@news.aaisp.net.uk>
On 2005-11-27 19:51:34 +0000, Peter Seibel <·····@gigamonkeys.com> said:

> verec <·····@mac.com> writes:
> 
>> On 2005-11-27 02:52:25 +0000, jayessay <······@foo.com> said:
>> 
>>> I haven't paid much attention to what you are actually trying to do,
>>> but now I notice that you are probably trying to reinvent what
>>> adjustable arrays.  Take another look at the CLHS (or CLTL2) on
>>> adjustable arrays and I bet they already are a superset of your
>>> dynarrays.
>> 
>> Yes. Pascal Bourguigon finally convinced me yesterday that I had
>> been too dumb, and was, indeed, reinventing the wheel. With the
>> insight that both of you just gave me, the array part of
>> CLTL2 suddenly has a new meaning. For some reason, I had been
>> blinded by the "displaced" array feature, and thought that it
>> was very cute, but not that useful in everyday situation, and
>> completely missed: the fill pointer, that arrays of dimension 1
>> were also called vectors, and thus completely skimmed over
>> vector-push & vector-push-extend ...
>> 
>> As a punishment, I just ordered Graham's ANSI CL, Norvig's
>> PAI and Siebel's PCL from Amazon, to complement my aging
>           ^^^^^^
> 
> Man, I'd probably get a lot more Google juice if my name wasn't so
> easy to misspell. ;-)
> 
> -Peter

Sorry. I've got a problem with 'i' and 'e'. I never ever know how to
spell 'receive' ... but with a little luck, I might not be the only
dyslexic on earth ... which means I've been helping _them_ finding you
on Google :-)

(Now, whether you want more of us as your readership ...)
-- 
JFB  ()
From: Pascal Bourguignon
Subject: Re: defstruct, BOA &key parameter ...
Date: 
Message-ID: <877javuluf.fsf@thalassa.informatimago.com>
verec <·····@mac.com> writes:

> On 2005-11-26 08:32:01 +0000, Pascal Bourguignon <····@mouse-potato.com> said:
>
>> verec <·····@mac.com> writes:
>>>> But I'd just write:
>>>> (defparameter *word-and-index*
>>>> (mapcar (lambda (type) (make-dynarray :type type))
>>>> '(character integer))) ; without the shadows!
>>> That's nice except that it binds the knowledge about all possible
>>> element types in one place, whereas
>>> 
>>>> (setf *words* (make-dynarray :type 'character))
>>> produces them on demand.
>> No, you missed the point: (make-dynarray :type type)
>> you can use a variable type.
>
> You're right.
>
>>>> Why should dynarray be a structure?
>>> Because I want a single handle to two (for now) bits of
>>> information, the actual array and the real count, irrespective
>>> of the array size.
>> What is the "real count"?
>
> The actual number of elements in the array as opposed to
> its capacity, which can be, in my implementation, up to
> 3/2 times as big as required (until trimmed down).
>
> (defun capacity (a)
> 	(length (dynarray-store a))
>
> and
>
> (defun actual-elemment-count (a)
> 	(dynarray-count a))
>
> such that it is always the case that
>
> (defun invariant (a)
> 	(>= (capacity a) (actual-elemment-count a)))
>
> returns T.
>
> This is so that I can grow the array by sizeable chunks rather
> than doing it every single time I have to append an item to it.
>
> Hence the need for a final trim operation when all the appending
> is done, so as to "reclaim" the excess storage.


That's exactly what an adjustable array with a fill-pointer does for
you gratis.  You are reimplementing them.


I'll write again my example, and this time please read it and try to
understand what happens:

(defun make-dynarray (&key (type t))
   (make-array 0 :adjustable t :fill-pointer 0
               :element-type    (typecase type
                                  ((or character number string) type)
                                  (otherwise `(or null ,type)))
               :initial-element (typecase type
                                   (character #\space)
                                   (number    0)
                                   (string    "")
                                   (otherwise nil))))


(defparameter *word* (make-dynarray :type (quote character)))
(vector-push-extend #\H *word*)
(vector-push-extend #\e *word*)
(vector-push-extend #\l *word*)
(vector-push-extend #\l *word*)
(vector-push-extend #\o *word*)

(defparameter *words* (make-dynarray :type (quote string)))
(vector-push-extend "Hello" *words*)
(vector-push-extend "world" *words*)

(defparameter *numbers* (make-dynarray :type (quote number)))
(vector-push-extend 0 *numbers*)
(vector-push-extend -1 *numbers*)
(vector-push-extend pi *numbers*)
(vector-push-extend (exp 1) *numbers*)

(defparameter *stuff* (make-dynarray :type (quote (or symbol string))))
(vector-push-extend 'hello  *stuff*)
(vector-push-extend "world" *stuff*)

(values  *word* *words* *numbers* *stuff*)
-->
#(#\H #\e #\l #\l #\o) ;
#("Hello" "world") ;
#(0 -1 3.1415926535897932385L0 2.7182817) ;
#(HELLO "world")


[87]> (mapcar (function length) (list  *word* *words* *numbers* *stuff*))
(5 2 4 2)
[88]> (mapcar (function array-dimensions) (list  *word* *words* *numbers* *stuff*))
((16) (16) (16) (16))


Note how LENGTH returns the number of elements that have been put in
the arrays, while ARRAY-DIMENSIONS returns the allocated sizes.

If you don't like the names, you can write:

(defun capacity              (a) (array-dimension a 0)) ; not length!
(defun actual-elemment-count (a) (length a))

For an array with a fill-pointer, length returns the fill-pointer.

If you need to adjust the size of the array to the actual contents,
you can write;

(defun size-to-fit (a) (adjust-array a (length a)))




I don't know you, but I'd rather spend a few weeks reading CLHS in
details than a few years re-implementing CL from scratch.


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

The world will now reboot.  don't bother saving your artefacts.
From: verec
Subject: Re: defstruct, BOA &key parameter ...
Date: 
Message-ID: <4388dadd$0$38039$5a6aecb4@news.aaisp.net.uk>
On 2005-11-26 21:43:04 +0000, Pascal Bourguignon <····@mouse-potato.com> said:

>> This is so that I can grow the array by sizeable chunks rather
>> than doing it every single time I have to append an item to it.
>> 
>> Hence the need for a final trim operation when all the appending
>> is done, so as to "reclaim" the excess storage.
> 
> That's exactly what an adjustable array with a fill-pointer does for
> you gratis.  You are reimplementing them.

Shock. %-[

> I'll write again my example, and this time please read it and try to
> understand what happens:

You've got me. I will.

> (vector-push-extend #\H *word*)

I'm going to look up that "vector-push-extend" thingie.

> I don't know you, but I'd rather spend a few weeks reading CLHS in
> details than a few years re-implementing CL from scratch.

Fair enough.

Many thanks, and my apolgies for having been so obtuse.
-- 
JFB  ()
From: verec
Subject: Re: defstruct, BOA &key parameter ...
Date: 
Message-ID: <4388dccc$0$38040$5a6aecb4@news.aaisp.net.uk>
On 2005-11-26 21:43:04 +0000, Pascal Bourguignon <····@mouse-potato.com> said:

>                                   (otherwise `(or null ,type)))

This one is a twister :)

I guess you wrote `(or null ,type) because neither `,type nor `(,type)
would have been syntactically legal, is that right?

But then, a backquote outside of a macro definition puzzles me.
-- 
JFB  ()
From: Pascal Bourguignon
Subject: Re: defstruct, BOA &key parameter ...
Date: 
Message-ID: <87veyfszum.fsf@thalassa.informatimago.com>
verec <·····@mac.com> writes:

> On 2005-11-26 21:43:04 +0000, Pascal Bourguignon <····@mouse-potato.com> said:
>
>>                                   (otherwise `(or null ,type)))
>
> This one is a twister :)
>
> I guess you wrote `(or null ,type) because neither `,type nor `(,type)
> would have been syntactically legal, is that right?
>
> But then, a backquote outside of a macro definition puzzles me.


The purpose is to be sure that NIL can be used as :initial-element;
otherwise I would need to be able to instanciate a value of the given
type, but even if you had a mean to do it for normal types
(structures, integers, etc),  this is not always possible because you
can make arrays with a NIL element type, for which there's no possible
value.  So instead of trying to guess an initial value in the
otherwise cases matching the given type, I change the element type to
include NIL.


* (defun g () (make-array 0 :element-type '(or null nil) :initial-element nil))

G

* (defun h () (make-array 0 :element-type 'nil :initial-element nil))
; in: LAMBDA NIL
;     (MAKE-ARRAY 0 :ELEMENT-TYPE 'NIL :INITIAL-ELEMENT NIL)
; 
; caught WARNING:
;   NIL is not a NIL (which is the UPGRADED-ARRAY-ELEMENT-TYPE of NIL).
;   See also:
;     The ANSI Standard, Function MAKE-ARRAY
;     The ANSI Standard, Function UPGRADED-ARRAY-ELEMENT-TYPE

; --> LET 
; ==>
;   (MAKE-ARRAY SB-C::DIMS :ELEMENT-TYPE 'NIL)
; 
; caught STYLE-WARNING:
;   The default initial element #:MU is not a NIL.
; 
; compilation unit finished
;   caught 1 WARNING condition
;   caught 1 STYLE-WARNING condition

H

* (defun d () (make-array 0 :element-type 'nil))
; in: LAMBDA NIL
;     (MAKE-ARRAY 0 :ELEMENT-TYPE 'NIL)
; 
; caught STYLE-WARNING:
;   The default initial element #:MU is not a NIL.
; 
; compilation unit finished
;   caught 1 STYLE-WARNING condition

D


So the writting `(or null ,type) is totally deliberate.
I could have written it as: (list 'or 'null type)
but the backquote form is clearer.

Backquote is an orthogonal concept to macros. 
It can be used anywhere you need to build a list following a pattern.

Backquote is the scripting level of the lisp language, where instead
of quoting the literals, you unquote the variables.

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
You're always typing.
Well, let's see you ignore my
sitting on your hands.
From: drewc
Subject: Re: defstruct, BOA &key parameter ...
Date: 
Message-ID: <Hx8if.630138$1i.590185@pd7tw2no>
verec wrote:
> On 2005-11-26 21:43:04 +0000, Pascal Bourguignon <····@mouse-potato.com> 
> said:
> 
>>                                   (otherwise `(or null ,type)))
> 
> 
> This one is a twister :)

Left Foot Blue!!

> 
> I guess you wrote `(or null ,type) because neither `,type nor `(,type)
> would have been syntactically legal, is that right?

Rather than guessing, a cursory glance at the CLHS should explain this:

"4.2.3 Type Specifiers

  Type specifiers can be symbols, classes, or lists"

now .. if we scroll down ..

"Figure 4-3. Standardized Compound Type Specifier Names

The next figure show the defined names that can be used as compound type 
specifier names but that cannot be used as atomic type specifiers.

and     mod  satisfies
eql     not  values
member  or"

Oh hrm ... OR is also a compound type specifier.

So then, clickety click.. the section on the OR typespec reads. :

"This denotes the set of all objects of the type determined by the union 
of the typespecs. For example, the type list by definition is the same 
as (or null cons)"

In the case that the preceeding is not clear :

UCW-USER> (deftype or-nil-t ()
	    '(or nil t))
OR-NIL-T
UCW-USER> (typep nil 'or-nil-t)
T
UCW-USER> (typep t 'or-nil-t)
T

> But then, a backquote outside of a macro definition puzzles me.

It shouldn't :). The backquote operator and macros are only 
superficially related, in that the former can be really useful when 
creating the latter. All backquote does is let you construct lists using 
a sort of 'template', which comes in really handy when using macros, but 
has many other uses outside of a macro. For example, if i wanted create 
a function that defines a class (not a macro), i'd still probably use 
backquote to create the defclass form , ie

(defun make-new-class (name supers slots)
   (eval `(defclass ,name ,supers ,slots)))

(make-class 'foo '(standard-class) '(baz bat))

no macros, but much easier, in a larger example, than doing it manually :
(defun make-new-class (name supers slots)
  (eval (list 'defclass name supers slots)))

In this particular case, using LIST is not too bad, but when 
constructing larger amounts of data/code, it gets messy fast. What is 
nice about using ` when creating code is that you can see the structure 
of the to-be-generated code quite clearly, when compared with the other 
methods.

If ` and , didn't exist, a lisp programmer would probably create them, 
using a macro :)

Here is a quick and dirty hack, which is very limited in that it doesn't 
handle nested unquoting, but serves to illustrate my point : i use 
BACKQUOTE as the macro, and ~ as the unquote character.

(defun unquotedp (symbol)
   (when (char-equal (elt (string symbol) 0) #\~)
     t))

(defun strip-unquote (symbol)
   (intern (string-left-trim "~" (symbol-name symbol))
           (symbol-package symbol)))

(defmacro backquote (&body forms)
   (let (pass-through)
     (labels ((transform-backquote (old-form)
                (if pass-through
                    (prog1 old-form (setf pass-through nil))
                    (cond
                      ((and (symbolp old-form) (unquotedp old-form))
                       (if (equal (length (symbol-name old-form)) 1)
                           (setf pass-through t)
                           (strip-unquote old-form))) 

                       ((atom old-form)
                        (list 'quote old-form))
                       ((listp old-form)
                        (loop for i in old-form collect
                              (transform-backquote i)))))))
              (loop for form in forms
                    collect (transform-backquote form) into tforms
                    finally
                    (return (cons 'list tforms))))))


You'll notice i dont use ` at all in the macro above, but rather just 
constuct the lists that make up the code using the standard methods.

And an example:

UCW-USER> (let (baz) (backquote foo bar ~baz ~"foo" ~(cons 1 2)))
(FOO BAR NIL T "foo" T (1 . 2))

No magic here, just a simple way to construct lists.

-- 
Drew Crampsie
drewc at tech dot coop
  "... the most advanced use of lisp in the field of bass lure sales"
	-- Xach on #lisp
From: verec
Subject: Re: defstruct, BOA &key parameter ...
Date: 
Message-ID: <438967ea$0$38038$5a6aecb4@news.aaisp.net.uk>
On 2005-11-27 02:07:35 +0000, drewc <·····@rift.com> said:

Drew, you're about to make me reconsider my stance on the usefulness
of the CLHS (as opposed to CLTL2) :-)

When it comes to computing, I felt that I knew quite a bit.
When I moved to CL, suddenly, I knew nothing anymore.

That's quite a humbling experience...

Many thanks for your comments.
-- 
JFB  ()
From: Kalle Olavi Niemitalo
Subject: Re: defstruct, BOA &key parameter ...
Date: 
Message-ID: <87k6ethl9l.fsf@Astalo.kon.iki.fi>
Pascal Bourguignon <····@mouse-potato.com> writes:

>>> (defun make-dynarray (&key (type t))
>>>    (make-array 0 :adjustable t :fill-pointer 0
>>>                :element-type    (typecase type
>>>                                   ((or character number string) type)
>>>                                   (otherwise `(or null ,type)))
>>>                :initial-element (typecase type
>>>                                    (character #\space)
>>>                                    (number    0)
>>>                                    (string    "")
>>>                                    (otherwise nil))))

That won't work right:

  (array-element-type (make-dynarray :type 'character))
  ;; => T

The parameter TYPE is bound to the symbol COMMON-LISP:CHARACTER.
The first TYPECASE then effectively checks
  (typep 'character (or character number string))
which returns false because CL:CHARACTER is not itself a character.

If you want to examine types in this manner, you could use SUBTYPEP:

  (cond ((subtypep type '(or character number string))
         type)
        (t
         `(or null ,type)))

Unfortunately, this also won't work: namely, if someone calls
(make-dynarray :type '(eql #\x)), then the type (eql #\x) is a
subtype of CHARACTER but does not include #\Space.  And I believe
an implementation could in principle support arrays specialized
to such a type.

So the check should really be the other way round:

  (defun make-dynarray (&key (type t))
    (multiple-value-bind (element-type initial-element)
        (cond ((typep #\Space type) (values type #\Space))
              ((typep 0       type) (values type 0))
              ((typep ""      type) (values type ""))
              (t (values `(or null ,type) nil)))
      (make-array 0 :adjustable t :fill-pointer 0
                  :element-type element-type
                  :initial-element initial-element)))

A shorter and sneakier way to write the same:

  (defun make-dynarray (&key (type t))
    (make-array 0 :adjustable t :fill-pointer 0
                :initial-element (cond
                                   ((typep #\Space type) #\Space)
                                   ((typep 0       type) 0)
                                   ((typep ""      type) "")
                                   (t (setq type `(or null ,type))
                                      nil))
                :element-type type))

However, this complexity is not always necessary...

> You must specify an explicit :initial-element because the default one
> is NIL and if you specify an :element-type, the :initial-element,
> default or not, must be compatible with the :element-type, to produce
> a conforming program.  If you don't do it, you fall into
> implementation specific behavior.

The following is a conforming program that always returns 42,
unless it hits an implementation limit:

  (length (make-array 42 :element-type 'character))

In COMMON-LISP:MAKE-ARRAY, :initial-element does not default to NIL.
(Perhaps you confused this with LET bindings, which do default to NIL;
the CLHS dictionary entry for LET even warns about mismatching types.)

If neither :initial-element nor :initial-contents is supplied to
MAKE-ARRAY, and the array is not displaced, then the elements of
the array will have undefined values; if any element is read
before it has been written, the consequences are undefined.  This
does not depend on whether the element type includes the symbol
NIL, and can even occur if the element type is T.

In practice however, several Lisp implementations fully
initialize the array during MAKE-ARRAY, so that they won't
confuse their garbage collectors.  Raymond Toy kindly explained
this to me in <···················@rtp.ericsson.se>.

The element type NIL is a somewhat special case, as usual.

Also, even if implementation-specific behaviour affects the
outcome of a program, it can still be conforming code.
(In C, it would not be "strictly conforming".)
From: Pascal Bourguignon
Subject: Re: defstruct, BOA &key parameter ...
Date: 
Message-ID: <87r791s5yf.fsf@thalassa.informatimago.com>
Kalle Olavi Niemitalo <···@iki.fi> writes:

> Pascal Bourguignon <····@mouse-potato.com> writes:
>
>>>> (defun make-dynarray (&key (type t))
>>>>    (make-array 0 :adjustable t :fill-pointer 0
>>>>                :element-type    (typecase type
>>>>                                   ((or character number string) type)
>>>>                                   (otherwise `(or null ,type)))
>>>>                :initial-element (typecase type
>>>>                                    (character #\space)
>>>>                                    (number    0)
>>>>                                    (string    "")
>>>>                                    (otherwise nil))))
>
> That won't work right:

Indeed. Thank you very much for the correction.


> [...the correct way, and explainations...]


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

This is a signature virus.  Add me to your signature and help me to live