From: Reini Urban
Subject: name prefixes for mapping for sideffects only
Date: 
Message-ID: <377f73b0.192670014@judy.x-ray.local>
[sorry kent, cross-posted to lisp and scheme to avoid double traffic]

I'm currently implementing an AutoLISP array library, which should
generate defstruct-alike some useful accessors and iterators for special
array slot types.
this should enable users to use their old arrays implemented as lists
(with costly destructive modifications, we have no SETCAR, SETCDR, SETF
nor SETNTH) and the new AutoCAD 2000 safearray primitive in existing
portable code.

something like:
(defarray name slots)

=>

 (make-<name> list-of-values)  => array	constructor
 (<name>-p    object) => T/nil  		predicate
 (<name>-length array) => length  		property
 (<name>-dim array) => list  			dimensions
 (<name>-ref array index) 	=> element  	array reader (fast)
?(<name>-elt array index) 	=> element  	checked reader
 (<name>-nth index array|list) 	=> element  	generic reader (fast)
 (<name>-set array index value) => value 	writer (fast)
 (<name>-position value array) => index  	search
!(<name>-insert value index array) => array  	insert random (costly)
 (<name>-delete index array) => array        	remove random (costly)
 (<name>-push value array) => array      	add at front (costly)
 (<name>-pop array) => value       		remove first (costly)
 (<name>->list array) => list-of-values      	converter
 (<name>-force-list array|list) => list-of-values
 (map<name> func array) => array		returns changed copy
 (map<name>-side func array) => undefined	sideeffects only
 (map<name>-into func array) => array		destructive modification

right now they are using handwritten loops, iterators with FOREACH or
MAPCAR and accessors with NTH. that's why I want to keep the syntax this
way. there's no way to add vector support to all the usual sequence
functions for now. special functions must be used unfortunately.
cdr-looping is very costly with arrays but widely used with lists;
this should be avoided.

i also could define the more general array/vector functions:
  aref,svref,vector-elt,... without generating all the specialized
methods (I did that before)
but there's a big problem to combine versions with and without native
vector support (pre AutoCAD 2000 and now)
and easy access to complicated data structures behind the array/list
abstraction, such as 
  (defarray "SEGMENT"
             '((p1 . ((x . vlax-vbDouble)(y . vlax-vbDouble)))
               (p2 . ((x . vlax-vbDouble)(y . vlax-vbDouble)))))
which is similar to a common lisp array of structures.
something like destructuring the :element-type property of vectors into
arrays.

(setq point1 '(0 0) point2 '(1 0))
(setq seg (make-segment (list (list point1 point2))))
 => #a<((p1-x p1-y) (p2-x p2-y)) ...> or #a<((0 0)(1 0))>

(aref seg 0) 
  => ((p1-x p1-y) (p2-x p2-y)), a segment, list of 2 points
(aref seg 0 1) 
  => (p2-x p2-y), a 2d point
(aref seg 0 1 0) 
  => p2-x, a real number, x of the endpoint of the first segment

sample usage: 
;;; vlax-vbDouble is one supported primitive type
(defarray "PTS-2D"
          '((x . vlax-vbDouble)(y . vlax-vbDouble)))

(mappts-2d 'print 
  (make-pts-2d list-of-2dpoints))

for sideeffects only we use commonly
  (foreach pt pts
    (print pt))
which could be changed to: 
  (foreach-pts-2d pt pts
    '((print pt)))

I don't know now which name to give to the mapping function used as the
fast not-copying version, for sideeffects only, returning nothing.
similar to the Common Lisp MAPC, MAP nil or the CLtL2 SERIES ITERATE
function.

FOREACH-<name> is a good construct. 
but it is a bit hard to implement beautifully because &rest is not
supported, forcing us to use a list as third argument.

(FOREACH-PTS-2D pt pts
  '((print pt) 
    ;...
   ))
=> 
this is expanded into something like this: 
(DOLIST would be better than REPEAT)
  
  (let ((i -1))
    (repeat (array-length pts)
      (let ((pt (pts-2d-ref pts (setq i (1+ i)))))
        (eval (cons 'progn '((print pt) ;...
                            ))))))
or in autolisp syntax, without LET:
  ((lambda (i)
    (repeat (array-length pts)
      ((lambda (pt)
        (print pt)
        ;; more body code...
       )
       (pts-2d-ref pts (setq i (1+ i)))))
    -1))

I only came up with the name MAP<name>-SIDEEFFECTS-ONLY or the shorter
MAP<name>-SIDE. 
MAPC<name> seems to be not the best mnemonic for this feature. 
MAP NIL is similar, so maybe MAP<name>-NIL is a fine name as well.
are there any other suggestions for a good mapping prefix for
sideeffects only?

(MAPPTS-2D-SIDE 
  '(lambda (pt) (print pt))
  pts)
(MAPCPTS-2D 
  '(lambda (pt) (print pt))
  pts)
(MAPPTS-2D-NIL 
  '(lambda (pt) (print pt))
  pts)
(FOREACH-PTS-2D pt pts
  '((print pt)))

---

my other problem is the naming and argument ordering of readers and
writers.
SETF is not available right now, but will be in the future.
vectors use -ELT (checked) and REF (fast) as postfix. NTH uses a
different argument ordering, but NTH is the only yet known primitive, so
I have to keep this. people will only use -NTH, not -REF nor -ELT,
because this needs the least changes in existing code and will keep
readability.

the setter functions can be named -PUT (used in existing ActiveX objects
setters) or -SET (not used now), the argument ordering (influenced by
SETF which is not nailed down yet)
could be 
 (<name>-set value index array) "set the new value at i to the array"
 which is currently widely used,
or
 (<name>-set array index value) "set in the array at i the new value" 
 SETF alike
or
 (<name>-set index array value) "set at i in the array the new value" 
 setnth alike

compared to the most likely to be used (<name>-NTH index array) I would
favor (<name>-SET index array value) to use this setter as direct
DEFSETF method for NTH.
Do other lisps/schemes use SETNTH there? (if not using the setf method)

But the object-first ordering (<name>-SET array index value) seems to be
the most common. right?

--
Reini Urban
http://xarch.tu-graz.ac.at/autocad/news/faq/autolisp.html

From: Kent M Pitman
Subject: Re: name prefixes for mapping for sideffects only
Date: 
Message-ID: <sfwemiothvf.fsf@world.std.com>
[ replying to comp.lang.lisp only
  http://world.std.com/~pitman/pfaq/cross-posting.html ]

······@xarch.tu-graz.ac.at (Reini Urban) writes:

> [sorry kent, cross-posted to lisp and scheme to avoid double traffic]

well, it's not like it's my newsgroup.  i'm trying to change the culture
on this but they have to do it voluntarily.  they don't answer to me.
 
i won't respond at all if I have to cross-post to do it.  So you get
potential double-traffic anyway as the price of having me involved at all.

[actual text of post being replied to omitted]

I regard it as an accomodation to history but otherwise a design
error that both NTH and GETHASH take their arguments in the wrong
order.  The container should, as a rule, precede the selector
according to widespread statistical preference, perhaps biased
by natural llanguage equivalents.  Most CL operators do this.
I often implement (TABLE-REF table key) in place of (GETHASH key table).
Sometimes I implement (LIST-REF list index) instead of (NTH index list)
but usually I just grin and bear it that NTH takes its args backwards.
After 20 years of using the language, I still frequently have to look
up the arg order of NTH just to be sure I'm not screwing it up.

I think it's acceptable but not really to be preferred if
 (GET-xxx ...args...)
functions have a companion
 (SET-xxx ...args... new-value)
The new-value-last convention is best only if you reasonably expect
you will never have any &REST in the ...args..., AND if you think
users will not accept on faith that SETF is coming and you'll need to
do things differently when it does.  The Lisp Machine did this
transition with New Flavors and :writable-instance-variables, and
there was considerable confusion over several years when (SETF xxx)
began to predominate as CLOS entered the picture--lots of users
complained they couldn't remember when it was new-value-first and when
it was new-value-last.  It's easier for people to remember a simple
rule and new-value-first, while confusing to motivate, is at least
easy to remember IF applied consistently.

Your question was somewhat long and I was mostly skimming.  I hope I
picked out the relevant parts and answered them in a way that's helpful.
If not, ask.  If some explanation for why I goofed is in the original
message, feel free to point me back to it rather than typing it all again.

On the matter of generated names, by the way, the conclusion in the
Maclisp/LispM/CL community, mostly due to early experience with packages
was that it was bad to write functions that generate symbol names and
it was better to have users ask for them.  Hence the switch from
 :writable-instance-variables
in Flavors as a Flavor-wide (i.e., class-wide) option in favor of
per-slot :accessor or :writer or whatever.  There were a lot of bugs
generated by "symbolconc" (as it was called back then) picking the
wrong package in various obscure circumstances.  It was thought better
to let the user force a proper package by typing the name of the symbol
in the right package.

Generating a zillion unrequested operators just clutters the namespace
and may not be the right thing.   I'm not a fan of it myself.  Sometimes
I find I think I've defined something and I haven't and then I can't
figure out why it's there anyway (sometimes with the wrong argument
conventions) and I find it's one of those "surprise" things contributed
by something like DEFSTRUCT...  That doesn't happen when you have to 
request the operator as CLOS does.
From: Marco Antoniotti
Subject: Crossposting to comp.lang.lisp and comp.lang.clos.
Date: 
Message-ID: <lwaetbs7w2.fsf_-_@copernico.parades.rm.cnr.it>
Kent M Pitman <······@world.std.com> writes:

> [ replying to comp.lang.lisp only
>   http://world.std.com/~pitman/pfaq/cross-posting.html ]
> 
> ······@xarch.tu-graz.ac.at (Reini Urban) writes:
> 
> > [sorry kent, cross-posted to lisp and scheme to avoid double traffic]
> 
> well, it's not like it's my newsgroup.  i'm trying to change the culture
> on this but they have to do it voluntarily.  they don't answer to me.
>  
> i won't respond at all if I have to cross-post to do it.  So you get
> potential double-traffic anyway as the price of having me involved
> at all.

How about cross posts to comp.lang.lisp and comp.lang.clos?

Cheers

PS. I admit my guilt as a crossposter.  I hope I redeemed myself
recently. :)


-- 
Marco Antoniotti ===========================================
PARADES, Via San Pantaleo 66, I-00186 Rome, ITALY
tel. +39 - 06 68 10 03 17, fax. +39 - 06 68 80 79 26
http://www.parades.rm.cnr.it/~marcoxa
From: Kent M Pitman
Subject: Re: Crossposting to comp.lang.lisp and comp.lang.clos.
Date: 
Message-ID: <sfwu2rjf4en.fsf@world.std.com>
Marco Antoniotti <·······@copernico.parades.rm.cnr.it> writes:

> How about cross posts to comp.lang.lisp and comp.lang.clos?

Doesn't work for me.  Each community self-selects and each community
has its own character.  Making a convincing argument to each of two
communities relies on the demographic of those two communities
overlapping more than I can really believe they do.

Also, the two communities have signed up for more and less detail
about a particular topic.  I might rephrase your question to be
"How about if I speak both at length and in summary?" in order for
you to see how the question strikes me. :-)

> PS. I admit my guilt as a crossposter.  I hope I redeemed myself
> recently. :)

It is important to keep your terminology correct.  In the US, our
ever-more-ill-educated newscasters, worried they will be sued for
casting aspersions on someone's good name, have learned to sprinkle
the word "alleged" into statements without thinking (or perhaps even
having the education to be able to think) about the meaning, resulting
in nonsensical statements like "He's allegedly accused of killing
someone."  or "The alleged defendant is arriving in the court."  These
statements have meaning but not the meaning the speakers probably
intend.  I am lobbying for a change in culture but I don't understand
myself to have yet succeeded, so it's not time to feel guilty ... yet.
I guess I think of it more like eating meat to a vegetarian or 
lusting in your heart to a Christian; you have to first elect the
faith and only then the community you've elected beat you up for your
otherwise legal desires. I'd LIKE crossposting to be something
for which people feel guilty, but officially it's still allowed.
Guilt is at your own discretion.
From: David Combs
Subject: Re: Crossposting to comp.lang.lisp and comp.lang.clos.
Date: 
Message-ID: <7m7v0r$es5@dfw-ixnews5.ix.netcom.com>
In article <···············@world.std.com>,
Kent M Pitman  <······@world.std.com> wrote:
<snip>
>
>It is important to keep your terminology correct.  In the US, our
>ever-more-ill-educated newscasters <snip>

Would like to hear your comment on existence of (human) editors
for newspapers (NYTimes, etc) and books.   :-(

David
From: Reini Urban
Subject: Re: name prefixes for mapping for sideffects only
Date: 
Message-ID: <3780caac.14410140@judy.x-ray.local>
Kent M Pitman <······@world.std.com> wrote:
>but usually I just grin and bear it that NTH takes its args backwards.

same for me with NTH and SUBST, compared to -REF or -ELT.

>After 20 years of using the language, I still frequently have to look
>up the arg order of NTH just to be sure I'm not screwing it up.

>I think it's acceptable but not really to be preferred if
> (GET-xxx ...args...)
>functions have a companion
> (SET-xxx ...args... new-value)
>The new-value-last convention is best only if you reasonably expect
>you will never have any &REST in the ...args..., AND if you think
>users will not accept on faith that SETF is coming and you'll need to
>do things differently when it does.  

that's a good argument. silently i prefered new-value-first, but didn't
dare to promote that. but haven't thought of &rest, esp. for selectors
with multidimensional arrays.
so i'll stick with (new object selectors...) which will remind users to
SUBST (new first) and helps in enabling &rest, which is more likely to
come than &key.
but on (SETF (get object selector...) newvalue) I will run into troubles
with sideeffects in any previous argument evaluation.
i'd rather prefer sideeffects to change newvalue than to change the
object or the selector. i'll have to investigate how likely this will
be. for sure it is no good style in SETF forms, but it looks better than
using the explicit setter. NTH is a mess here.

  obj => (0 a 1 b 2 c 3 d)

  (SETNTH (prog1
            (setq i (position 'a obj))
            (setq obj (cons (1+ (nth (1- i) obj)) obj)))
    ;; obj => (1 a 0 a 1 b 2 c 3 d)
          i obj)

  obj => (1 1 0 a 1 b 2 c 3 d)

or:
  (setf (nth (setq i (position 'a obj)) obj)
        (cons (1+ (nth (1- i) obj)) obj))

  obj => ??

>If some explanation for why I goofed is in the original
>message, feel free to point me back to it rather than typing it all again.

my basic question was a good name for a FOREACH style of loop but as MAP
suffix, such as MAPC<name>, MAP<name>-NIL, MAP<name>-SIDEEFFECTS-ONLY or
MAP<name>-SIDE. Maybe this happened somewhere before and I can benefit
from previous experiences with this naming. personally i don't like any
of them, favoring FOREACH-<name>.

>On the matter of generated names, by the way, the conclusion in the
>Maclisp/LispM/CL community, mostly due to early experience with packages
>was that it was bad to write functions that generate symbol names and
>it was better to have users ask for them.  Hence the switch from
> :writable-instance-variables
>in Flavors as a Flavor-wide (i.e., class-wide) option in favor of
>per-slot :accessor or :writer or whatever.  

hmm, that's true. i also dislike that defstruct generates too much, and
with defclass i see in the definition which functions are generated.

>There were a lot of bugs
>generated by "symbolconc" (as it was called back then) picking the
>wrong package in various obscure circumstances.  It was thought better
>to let the user force a proper package by typing the name of the symbol
>in the right package.

the namespace problem is solved thanksfully. LOAD is not that
complicated in AutoLISP than in CL. though it is dynamic and can be a
nightmare to debug.

>Generating a zillion unrequested operators just clutters the namespace
>and may not be the right thing.   I'm not a fan of it myself.  Sometimes
>I find I think I've defined something and I haven't and then I can't
>figure out why it's there anyway (sometimes with the wrong argument
>conventions) and I find it's one of those "surprise" things contributed
>by something like DEFSTRUCT...  That doesn't happen when you have to 
>request the operator as CLOS does.

agreed. but a few basic operators should be created automatically, such
as the maker, the predicate and a basic slot-setter. specialized
slot-accessors and other operators only on request. it should be more
convenient than C++, as precise as CLOS, and less bloated than
defstruct.

so i will use it defclass alike, only generating the maker, the
predicate and the basic getters and setters (similar to slot-value), but
copiers, iterators and others only optionally.

--
Reini Urban
http://xarch.tu-graz.ac.at/autocad/news/faq/autolisp.html