From: J Kenneth King
Subject: abstract sequences
Date: 
Message-ID: <873aiwl4aj.fsf@agentultra.com>
I have a very, very simple example function that will reverse a vector
(in the geometric sense).

(defun reverse-vector (vec)
  (loop for x in vec
    collecting (- x)))

This very simple function does a very simple thing and I don't think I
need to explain it...

Curiously, if I throw it a (lisp) vector instead of a list argument,
there is a type error. Granted we all know lisp, so this is an
expected error.

However, I am curious if there's a way to make my function more
generic.  It should be happy in life believing that its argument is a
sequence of numbers. Is it important for this function to be concerned
with the type of sequence? Does the type of sequence have any
relevance to its operation?

If this were a function of many as part of a geometry library, would
it make sense to force users of the library to represent their vector
data as a specific type or should they be more free to choose their
own?

Loving lisp, btw. It's good stuff.

From: Joshua Taylor
Subject: Re: abstract sequences
Date: 
Message-ID: <227ad1c4-abc2-4121-b3a1-de5224b533ca@l42g2000hsc.googlegroups.com>
On Oct 16, 3:37 pm, J Kenneth King <·····@agentultra.com> wrote:
> I have a very, very simple example function that will reverse a vector
> (in the geometric sense).
>
> (defun reverse-vector (vec)
>   (loop for x in vec
>     collecting (- x)))
>
> This very simple function does a very simple thing and I don't think I
> need to explain it...
>
> Curiously, if I throw it a (lisp) vector instead of a list argument,
> there is a type error. Granted we all know lisp, so this is an
> expected error.
>
> However, I am curious if there's a way to make my function more
> generic.  It should be happy in life believing that its argument is a
> sequence of numbers. Is it important for this function to be concerned
> with the type of sequence? Does the type of sequence have any
> relevance to its operation?
>
> If this were a function of many as part of a geometry library, would
> it make sense to force users of the library to represent their vector
> data as a specific type or should they be more free to choose their
> own?
>
> Loving lisp, btw. It's good stuff.

Unfortunately (for this case), loop can expand to vector or list
specific code depending on "in" vs. "across". Macroexpansions from
Lispworks are presented below.

CL-USER 1 > (pprint (macroexpand '(loop for x in vec collecting (-
x))))

(BLOCK NIL
  (MACROLET ((LOOP-FINISH () '(GO #:|end-loop-884|)))
    (LET ((X NIL) (#:|tail-887| VEC) (#:|by-888| 'SYSTEM:CDR$CONS))
      (LET ((#:|accumulator-885| (LIST NIL)))
        (DECLARE (TYPE LIST #:|accumulator-885|))
        (LET ((#:|aux-var-891| #:|accumulator-885|))
          (TAGBODY
           #:|begin-loop-883| (PROGN
                                (WHEN (OR (ENDP #:|tail-887|)) (GO #:|
end-loop-884|))
                                (LET ((#:|temp-890| (FUNCALL #:|
by-888| #:|tail-887|))
                                      (#:|temp-889| (SYSTEM:CAR$CONS
#:|tail-887|)))
                                  (SETQ X #:|temp-889|)
                                  (SETQ #:|tail-887| #:|temp-890|)))
                   (SETQ #:|aux-var-891| (CDR (RPLACD (THE CONS #:|aux-
var-891|) (LIST (- X)))))
                   (GO #:|begin-loop-883|)
           #:|end-loop-884| (RETURN-FROM NIL (CDR (THE CONS #:|
accumulator-885|)))))))))

CL-USER 2 > (pprint (macroexpand '(loop for x across vec collecting (-
x))))

(BLOCK NIL
  (MACROLET ((LOOP-FINISH () '(GO #:|end-loop-893|)))
    (LET ((#:|across-expr-896| VEC) (#:|across-length-897| 0) (#:|
across-counter-898| 0) (X NIL))
      (DECLARE (TYPE VECTOR #:|across-expr-896|))
      (DECLARE (TYPE FIXNUM #:|across-length-897|))
      (DECLARE (TYPE FIXNUM #:|across-counter-898|))
      (LET ((#:|accumulator-894| (LIST NIL)))
        (DECLARE (TYPE LIST #:|accumulator-894|))
        (LET ((#:|aux-var-901| #:|accumulator-894|))
          (TAGBODY (PROGN
                     (LET ((#:|temp-899| (SYSTEM::ACROSS-CHECK-IS-
VECTOR #:|across-expr-896|)))
                       (SETQ #:|across-length-897| #:|temp-899|))
                     (WHEN (OR (= 0 #:|across-length-897|)) (GO #:|end-
loop-893|))
                     (SETQ X (AREF #:|across-expr-896| 0)))
           #:|begin-loop-892| (SETQ #:|aux-var-901|
                                    (CDR (RPLACD (THE CONS #:|aux-
var-901|) (LIST (- X)))))
                   (PROGN
                     (LET ((#:|temp-900| (THE FIXNUM (1+ #:|across-
counter-898|))))
                       (SETQ #:|across-counter-898| #:|temp-900|))
                     (WHEN (OR (= #:|across-counter-898| #:|across-
length-897|)) (GO #:|end-loop-893|))
                     (SETQ X (AREF #:|across-expr-896| #:|across-
counter-898|)))
                   (GO #:|begin-loop-892|)
           #:|end-loop-893| (RETURN-FROM NIL (CDR (THE CONS #:|
accumulator-894|)))))))))

Now, in the your code, a list is returned when a list is passed in. If
the return type were always to be the same, then, say (map 'list '-
sequence) would be fine. You'd always get a list back. (map
'vector ...) would always give a vector. Using type-of, you could get
the results to match, as follows:

CL-USER 3 > (type-of #(1 2 3))
(SIMPLE-VECTOR 3)

CL-USER 4 > (type-of '(1 2 3))
CONS

CL-USER 5 >
(defun reverse-geometric (seq)
  (map (type-of seq) '- seq))
REVERSE-GEOMETRIC

CL-USER 6 > (reverse-geometric '(1 2 3))
(-1 -2 -3)

CL-USER 7 > (reverse-geometric #(1 2 3))
#(-1 -2 -3)

I think this should be portable, at least insofar as getting back
vectors and lists, based on the interaction of TYPE-OF and how MAP
handles its first argument.

From the documentation on MAP:

"If the result-type is a subtype of list, the result will be a list.

If the result-type is a subtype of vector, then if the implementation
can determine the element type specified for the result-type, the
element type of the resulting array is the result of upgrading that
element type; or, if the implementation can determine that the element
type is unspecified (or *), the element type of the resulting array is
t; otherwise, an error is signaled."

and from that on TYPE-OF:

"1. For any object that is an element of some built-in type:
    a. the type returned is a recognizable subtype of that built-in
type.
    b. the type returned does not involve and, eql, member, not, or,
satisfies, or values. "

Another alternative would be to use CLOS:

CL-USER 15 >
(defmethod reverse-geometric2 ((seq vector))
  (map 'vector '- seq)) ; or (loop for x across)
#<STANDARD-METHOD REVERSE-GEOMETRIC2 NIL (VECTOR) 21520F43>

CL-USER 16 >
(defmethod reverse-geometric2 ((seq list))
  (map 'list '- seq)) ; or (loop for x in ...) or (mapcar '- seq)
#<STANDARD-METHOD REVERSE-GEOMETRIC2 NIL (LIST) 20103A0B>

CL-USER 17 > (reverse-geometric2 '(1 2 3))
(-1 -2 -3)

CL-USER 18 > (reverse-geometric2 #(1 2 3))
#(-1 -2 -3)

I'll be interested to see other people's approaches too.
From: J Kenneth King
Subject: Re: abstract sequences
Date: 
Message-ID: <87y70ojoje.fsf@agentultra.com>
Joshua Taylor <···········@gmail.com> writes:

> On Oct 16, 3:37 pm, J Kenneth King <·····@agentultra.com> wrote:
>> I have a very, very simple example function that will reverse a vector
>> (in the geometric sense).
>>
>> (defun reverse-vector (vec)
>>   (loop for x in vec
>>     collecting (- x)))
>>
>> This very simple function does a very simple thing and I don't think I
>> need to explain it...
>>
>> Curiously, if I throw it a (lisp) vector instead of a list argument,
>> there is a type error. Granted we all know lisp, so this is an
>> expected error.
>>
>> However, I am curious if there's a way to make my function more
>> generic.  It should be happy in life believing that its argument is a
>> sequence of numbers. Is it important for this function to be concerned
>> with the type of sequence? Does the type of sequence have any
>> relevance to its operation?
>>
>> If this were a function of many as part of a geometry library, would
>> it make sense to force users of the library to represent their vector
>> data as a specific type or should they be more free to choose their
>> own?
>>
>> Loving lisp, btw. It's good stuff.
>
> Unfortunately (for this case), loop can expand to vector or list
> specific code depending on "in" vs. "across". Macroexpansions from
> Lispworks are presented below.
>
> CL-USER 1 > (pprint (macroexpand '(loop for x in vec collecting (-
> x))))
>
> (BLOCK NIL
>   (MACROLET ((LOOP-FINISH () '(GO #:|end-loop-884|)))
>     (LET ((X NIL) (#:|tail-887| VEC) (#:|by-888| 'SYSTEM:CDR$CONS))
>       (LET ((#:|accumulator-885| (LIST NIL)))
>         (DECLARE (TYPE LIST #:|accumulator-885|))
>         (LET ((#:|aux-var-891| #:|accumulator-885|))
>           (TAGBODY
>            #:|begin-loop-883| (PROGN
>                                 (WHEN (OR (ENDP #:|tail-887|)) (GO #:|
> end-loop-884|))
>                                 (LET ((#:|temp-890| (FUNCALL #:|
> by-888| #:|tail-887|))
>                                       (#:|temp-889| (SYSTEM:CAR$CONS
> #:|tail-887|)))
>                                   (SETQ X #:|temp-889|)
>                                   (SETQ #:|tail-887| #:|temp-890|)))
>                    (SETQ #:|aux-var-891| (CDR (RPLACD (THE CONS #:|aux-
> var-891|) (LIST (- X)))))
>                    (GO #:|begin-loop-883|)
>            #:|end-loop-884| (RETURN-FROM NIL (CDR (THE CONS #:|
> accumulator-885|)))))))))
>
> CL-USER 2 > (pprint (macroexpand '(loop for x across vec collecting (-
> x))))
>
> (BLOCK NIL
>   (MACROLET ((LOOP-FINISH () '(GO #:|end-loop-893|)))
>     (LET ((#:|across-expr-896| VEC) (#:|across-length-897| 0) (#:|
> across-counter-898| 0) (X NIL))
>       (DECLARE (TYPE VECTOR #:|across-expr-896|))
>       (DECLARE (TYPE FIXNUM #:|across-length-897|))
>       (DECLARE (TYPE FIXNUM #:|across-counter-898|))
>       (LET ((#:|accumulator-894| (LIST NIL)))
>         (DECLARE (TYPE LIST #:|accumulator-894|))
>         (LET ((#:|aux-var-901| #:|accumulator-894|))
>           (TAGBODY (PROGN
>                      (LET ((#:|temp-899| (SYSTEM::ACROSS-CHECK-IS-
> VECTOR #:|across-expr-896|)))
>                        (SETQ #:|across-length-897| #:|temp-899|))
>                      (WHEN (OR (= 0 #:|across-length-897|)) (GO #:|end-
> loop-893|))
>                      (SETQ X (AREF #:|across-expr-896| 0)))
>            #:|begin-loop-892| (SETQ #:|aux-var-901|
>                                     (CDR (RPLACD (THE CONS #:|aux-
> var-901|) (LIST (- X)))))
>                    (PROGN
>                      (LET ((#:|temp-900| (THE FIXNUM (1+ #:|across-
> counter-898|))))
>                        (SETQ #:|across-counter-898| #:|temp-900|))
>                      (WHEN (OR (= #:|across-counter-898| #:|across-
> length-897|)) (GO #:|end-loop-893|))
>                      (SETQ X (AREF #:|across-expr-896| #:|across-
> counter-898|)))
>                    (GO #:|begin-loop-892|)
>            #:|end-loop-893| (RETURN-FROM NIL (CDR (THE CONS #:|
> accumulator-894|)))))))))
>
> Now, in the your code, a list is returned when a list is passed in. If
> the return type were always to be the same, then, say (map 'list '-
> sequence) would be fine. You'd always get a list back. (map
> 'vector ...) would always give a vector. Using type-of, you could get
> the results to match, as follows:
>
> CL-USER 3 > (type-of #(1 2 3))
> (SIMPLE-VECTOR 3)
>
> CL-USER 4 > (type-of '(1 2 3))
> CONS
>
> CL-USER 5 >
> (defun reverse-geometric (seq)
>   (map (type-of seq) '- seq))
> REVERSE-GEOMETRIC
>
> CL-USER 6 > (reverse-geometric '(1 2 3))
> (-1 -2 -3)
>
> CL-USER 7 > (reverse-geometric #(1 2 3))
> #(-1 -2 -3)
>
> I think this should be portable, at least insofar as getting back
> vectors and lists, based on the interaction of TYPE-OF and how MAP
> handles its first argument.
>
> From the documentation on MAP:
>
> "If the result-type is a subtype of list, the result will be a list.
>
> If the result-type is a subtype of vector, then if the implementation
> can determine the element type specified for the result-type, the
> element type of the resulting array is the result of upgrading that
> element type; or, if the implementation can determine that the element
> type is unspecified (or *), the element type of the resulting array is
> t; otherwise, an error is signaled."
>
> and from that on TYPE-OF:
>
> "1. For any object that is an element of some built-in type:
>     a. the type returned is a recognizable subtype of that built-in
> type.
>     b. the type returned does not involve and, eql, member, not, or,
> satisfies, or values. "
>
> Another alternative would be to use CLOS:
>
> CL-USER 15 >
> (defmethod reverse-geometric2 ((seq vector))
>   (map 'vector '- seq)) ; or (loop for x across)
> #<STANDARD-METHOD REVERSE-GEOMETRIC2 NIL (VECTOR) 21520F43>
>
> CL-USER 16 >
> (defmethod reverse-geometric2 ((seq list))
>   (map 'list '- seq)) ; or (loop for x in ...) or (mapcar '- seq)
> #<STANDARD-METHOD REVERSE-GEOMETRIC2 NIL (LIST) 20103A0B>
>
> CL-USER 17 > (reverse-geometric2 '(1 2 3))
> (-1 -2 -3)
>
> CL-USER 18 > (reverse-geometric2 #(1 2 3))
> #(-1 -2 -3)
>
> I'll be interested to see other people's approaches too.

Very enlightening!

Thanks for taking the time for posting such a great reply! :)
From: John Thingstad
Subject: Re: abstract sequences
Date: 
Message-ID: <op.ui5z9zu4ut4oq5@pandora.alfanett.no>
P� Thu, 16 Oct 2008 21:55:55 +0200, skrev Joshua Taylor  
<···········@gmail.com>:

>
> I'll be interested to see other people's approaches too.

I think you covered the basics. I might add that iterate has a by-sequence  
option where loop doesn't. Under the hood it is still elt, the genereric  
accessors that works on both lists and vectors. Slow though. So I would  
probably go for the defmethod version for succinctness and efficiency.

--------------
John Thingstad
From: Joshua Taylor
Subject: Re: abstract sequences
Date: 
Message-ID: <f5717a94-a534-4fa9-90fc-a816ec961559@t42g2000hsg.googlegroups.com>
On Oct 17, 6:47 am, "John Thingstad" <·······@online.no> wrote:
> På Thu, 16 Oct 2008 21:55:55 +0200, skrev Joshua Taylor  
> <···········@gmail.com>:
> > I'll be interested to see other people's approaches too.
>
> I think you covered the basics. I might add that iterate has a by-sequence  
> option where loop doesn't. Under the hood it is still elt, the genereric  
> accessors that works on both lists and vectors. Slow though. So I would  
> probably go for the defmethod version for succinctness and efficiency.
> --------------
> John Thingstad

Well, then I guess here's a way to ask how /other/ people have
implemented sequence-type dependent functions. Getting the result of
the arithmetic-inverse described earlier in the same type of sequence
as the input required either TYPE-OF, TYPECASE, method dispatch, or
something along those lines. To do it destructively requires none of
those. E.g.,

CL-USER 1 >
(defun narithmetric-inverse (seq)
  (map-into seq '- seq))
NARITHMETRIC-INVERSE

Let's look at the results on some sizable inputs. (I'm running
Lispworks 5.1.1 on OS X 10.5.5 on a G5.)

CL-USER 2 > (compile 'narithmetric-inverse)
NARITHMETRIC-INVERSE
NIL
NIL

CL-USER 3 > (defvar big-list (make-list 1000000 :initial-element 2))
BIG-LIST

CL-USER 4 > (defvar big-vector (make-array 1000000 :initial-element
2))
BIG-VECTOR

CL-USER 5 > (time (prog1 nil (narithmetric-inverse big-list)))
Timing the evaluation of (PROG1 NIL (NARITHMETRIC-INVERSE BIG-LIST))

User time    =        0.067
System time  =        0.000
Elapsed time =        0.069
Allocation   = 72 bytes
1 Page faults
NIL

CL-USER 6 > (time (prog1 nil (narithmetric-inverse big-vector)))
Timing the evaluation of (PROG1 NIL (NARITHMETRIC-INVERSE BIG-VECTOR))

User time    =        0.075
System time  =        0.000
Elapsed time =        0.077
Allocation   = 0 bytes
0 Page faults
NIL

Ok, so we get a better time with the list, but page faults and memory
allocation, whereas no allocation and no page faults for the vector.
Maybe if we increase the size of the inputs by an order of
magnitude...

CL-USER 7 > (defvar bigger-list (make-list 10000000 :initial-element
2))
BIGGER-LIST

CL-USER 8 > (defvar bigger-vector (make-array 10000000 :initial-
element 2))
BIGGER-VECTOR

CL-USER 9 > (time (prog1 nil (narithmetric-inverse bigger-list)))
Timing the evaluation of (PROG1 NIL (NARITHMETRIC-INVERSE BIGGER-
LIST))

User time    =        0.546
System time  =        0.004
Elapsed time =        0.583
Allocation   = 5216 bytes
2 Page faults
NIL

CL-USER 10 > (time (prog1 nil (narithmetric-inverse bigger-vector)))
Timing the evaluation of (PROG1 NIL (NARITHMETRIC-INVERSE BIGGER-
VECTOR))

User time    =        0.611
System time  =        0.002
Elapsed time =        0.615
Allocation   = 72 bytes
0 Page faults
NIL

Now we've got some allocation in both cases, but much more on the list
side. Still no page faults on the vector. But the list case is still a
bit faster in elapsed time.

At any rate, the timing wasn't the issue so much as that there are CL
functions that operate on sequences generically, and seem to do it
more efficiently than just using ELT. So how do various implementers
do this? I don't have the source to LW, so I went to sourceforge and
grabbed SBCL 1.0.21, and I find the following. (To be fair, their
implementation of MAP looks like it's had much more love.) CMUCL still
has this same implementaton of MAP-INTO:

;;; KLUDGE: MAP has been rewritten substantially since the fork from
;;; CMU CL in order to give reasonable performance, but this
;;; implementation of MAP-INTO still has the same problems as the old
;;; MAP code. Ideally, MAP-INTO should be rewritten to be efficient in
;;; the same way that the corresponding cases of MAP have been
;;; rewritten. Instead of doing it now, though, it's easier to wait
;;; until we have DYNAMIC-EXTENT, at which time it should become
;;; extremely easy to define a reasonably efficient MAP-INTO in terms
;;; of (MAP NIL ..). -- WHN 20000920
(defun map-into (result-sequence function &rest sequences)
  (let* ((fp-result
          (and (arrayp result-sequence)
               (array-has-fill-pointer-p result-sequence)))
         (len (apply #'min
                     (if fp-result
                         (array-dimension result-sequence 0)
                         (length result-sequence))
                     (mapcar #'length sequences))))

    (when fp-result
      (setf (fill-pointer result-sequence) len))

    (let ((really-fun (%coerce-callable-to-fun function)))
      (dotimes (index len)
        (setf (elt result-sequence index)
              (apply really-fun
                     (mapcar (lambda (seq) (elt seq index))
                             sequences))))))
  result-sequence)

So SBCL isn't doing sequence-type specific code for MAP-INTO. And we
get results like this (SBCL 1.0.2):

CL-USER> (let ((l (make-list 100000 :initial-element 2)))
	   (time (prog1 nil (narithmetic-inverse l))))
Evaluation took:
  42.641 seconds of real time
  41.358864 seconds of user run time
  0.326722 seconds of system run time
  0 calls to %EVAL
  0 page faults and
  1,597,440 bytes consed.
NIL

CL-USER> (let ((v (make-array 100000 :initial-element 2)))
	   (time (prog1 nil (narithmetic-inverse v))))
Evaluation took:
  0.05 seconds of real time
  0.040844 seconds of user run time
  0.004547 seconds of system run time
  0 calls to %EVAL
  0 page faults and
  1,601,536 bytes consed.
NIL

(Maybe someone will see this and fix it...) CCL, on the other hand,
does some fancier things, checking sequence types with listp:

(defun map-into (result-sequence function &rest sequences)
  (declare (dynamic-extent sequences))
  (let* ((nargs (list-length sequences))
         (temp (make-list (length sequences)))
         (maxcnt (seq-dispatch result-sequence (length result-
sequence) (array-total-size result-sequence)))
         (rseq result-sequence))
    (declare (fixnum cnt nargs maxcnt))
    (declare (dynamic-extent temp))
    ; this declaration is maybe bogus
    (dolist (seq sequences)
      (let ((len (length seq)))
        (declare (fixnum len))
        (if (< len maxcnt)(setq maxcnt len))))
    (dotimes (cnt maxcnt)
      (let ((args temp)(seqs sequences))
        (dotimes (i nargs)
          (let ((seq (%car seqs)))
            (cond ((listp seq)
                   (%rplaca seqs (%cdr seq))
                   (%rplaca args (%car seq)))
                  (t (%rplaca args (aref seq cnt)))))
          (setq args (%cdr args))
          (setq seqs (%cdr seqs))))
      (let ((res (apply function temp)))
        (cond ((consp rseq)
               (%rplaca rseq res)
               (setq rseq (%cdr rseq)))
              (t (setf (aref result-sequence cnt) res)))))
    (when (and (not (listp result-sequence))
               (array-has-fill-pointer-p result-sequence))
      (setf (fill-pointer result-sequence) maxcnt))
    result-sequence))

And this yields results like

CL-USER> (let ((s (make-list 10000000 :initial-element 2)))
	   (time (prog1 nil (narithmetic-inverse s))))
(PROG1 NIL (NARITHMETIC-INVERSE S)) took 1,421,163 microseconds
(1.421163 seconds) to run
                    with 2 available CPU cores.
During that period, 1,409,310 microseconds (1.409310 seconds) were
spent in user mode
                    7,037 microseconds (0.007037 seconds) were spent
in system mode
NIL

CL-USER> (let ((s (make-array 10000000 :initial-element 2)))
	   (time (prog1 nil (narithmetic-inverse s))))
(PROG1 NIL (NARITHMETIC-INVERSE S)) took 1,144,267 microseconds
(1.144267 seconds) to run
                    with 2 available CPU cores.
During that period, 1,134,489 microseconds (1.134489 seconds) were
spent in user mode
                    5,325 microseconds (0.005325 seconds) were spent
in system mode
NIL

But unfortunately, this small investigation makes it seem that even
Lisp vendors/implementers are stuck writing code in this style that
just makes conditionals based on the particular sequence type. Anyone
have any good examples of a different approach?
From: Juho Snellman
Subject: Re: abstract sequences
Date: 
Message-ID: <87bpxjp96m.fsf@vasara.proghammer.com>
Joshua Taylor <···········@gmail.com> writes:
> But unfortunately, this small investigation makes it seem that even
> Lisp vendors/implementers are stuck writing code in this style that
> just makes conditionals based on the particular sequence type. Anyone
> have any good examples of a different approach?

Obviously some form of conditionals (explicit ones or via dynamic
dispatch) will have to happen at some point. Another strategy is to
have a generic sequence iteration protocol. For example, have a look
at the expansion (in sbcl) of:

  (loop for x being the elements of arbitrary-sequence collect x)

Which works on lists, vectors, and any user-defined sequence types,
with a single generic expansion, without using elt.

-- 
Juho Snellman
From: Joshua Taylor
Subject: Re: abstract sequences
Date: 
Message-ID: <f8b3bd8f-701e-44c6-b7c7-8ed8a6a33f4b@j68g2000hsf.googlegroups.com>
On Oct 17, 10:50 am, Juho Snellman <······@iki.fi> wrote:
> Joshua Taylor <···········@gmail.com> writes:
> > But unfortunately, this small investigation makes it seem that even
> > Lisp vendors/implementers are stuck writing code in this style that
> > just makes conditionals based on the particular sequence type. Anyone
> > have any good examples of a different approach?
>
> Obviously some form of conditionals (explicit ones or via dynamic
> dispatch) will have to happen at some point. Another strategy is to
> have a generic sequence iteration protocol. For example, have a look
> at the expansion (in sbcl) of:
>
>   (loop for x being the elements of arbitrary-sequence collect x)
>
> Which works on lists, vectors, and any user-defined sequence types,
> with a single generic expansion, without using elt.
>
> --
> Juho Snellman

Of course, I didn't mean to suggest that the code wouldn't have to be
conditionalized anywhere---I had just, perhaps naively, expected that
sequence abstractions to avoid making the user aware of it would be
more common. That SBCL example seems to be an extension to Common Lisp
(the LOOP macro only uses BEING in hash and package iteration
constructs), but is exactly the sort of thing that I was wondering
about---an extension to make sequence abstract less painful. I'm just
surprised that some of these aren't used down at the CL standard
library implementation level.

-- //JT
From: Juho Snellman
Subject: Re: abstract sequences
Date: 
Message-ID: <877i87p53e.fsf@vasara.proghammer.com>
Joshua Taylor <···········@gmail.com> writes: 
> Of course, I didn't mean to suggest that the code wouldn't have to be
> conditionalized anywhere---I had just, perhaps naively, expected that
> sequence abstractions to avoid making the user aware of it would be
> more common. That SBCL example seems to be an extension to Common Lisp
> (the LOOP macro only uses BEING in hash and package iteration
> constructs), but is exactly the sort of thing that I was wondering
> about---an extension to make sequence abstract less painful. I'm just
> surprised that some of these aren't used down at the CL standard
> library implementation level.

The real point of the extension is definition of new sequence types
[*], which standard CL provides no facilities for. But there's a
reason to not use it internally: optimizing away the generic iterator
abstraction is a lot harder than just doing a single dispatch to a
list or vector implementation.

[*] www.doc.gold.ac.uk/~mas01cr/papers/ilc2007/sequences-20070301.pdf 

-- 
Juho Snellman
From: Kaz Kylheku
Subject: Re: abstract sequences
Date: 
Message-ID: <20081016132602.287@gmail.com>
On 2008-10-16, J Kenneth King <·····@agentultra.com> wrote:
>
> I have a very, very simple example function that will reverse a vector
> (in the geometric sense).

This is called (arithmetic) inverse, not reverse.

> (defun reverse-vector (vec)
>   (loop for x in vec
>     collecting (- x)))

LOOP breaks the sequence abstraction by requiring different syntax
for looping over vectors and lists:

 (loop for x across #(1 2 3) ...)

 (loop for x in '(1 2 3) ...)

> This very simple function does a very simple thing and I don't think I
> need to explain it...

The MAP function will iterate over any sequence, but you have to specify
the output sequence type.

  ;; iterate over list, results into vector
  (map 'vector #'- '(1 2 3)) -> #(-1 -2 -3)

  ;; iterate over vector, results into vector
  (map 'vector #'- #(1 2 3)) -> #(-1 -2 -3)

  ;; iterate over string, results into string
  (map 'string #'char-upcase "abc") -> "ABC"

If you want the resulting sequence to be the same as the incoming sequence,
try this:

 (defun inverse (vec)
   (map (type-of vec) #'- vec))

> Curiously, if I throw it a (lisp) vector instead of a list argument,
> there is a type error. Granted we all know lisp, so this is an
> expected error.

Not sure what you mean; the error is expected if you know that the loop
for-as-in-list subclause (CLHS 6.1.2.1.2) clause takes only lists.
From: J Kenneth King
Subject: Re: abstract sequences
Date: 
Message-ID: <87ej2g6x38.fsf@agentultra.com>
Kaz Kylheku <········@gmail.com> writes:

> On 2008-10-16, J Kenneth King <·····@agentultra.com> wrote:
>>
>> I have a very, very simple example function that will reverse a vector
>> (in the geometric sense).
>
> This is called (arithmetic) inverse, not reverse.
>
>> (defun reverse-vector (vec)
>>   (loop for x in vec
>>     collecting (- x)))
>
> LOOP breaks the sequence abstraction by requiring different syntax
> for looping over vectors and lists:
>
>  (loop for x across #(1 2 3) ...)
>
>  (loop for x in '(1 2 3) ...)
>
>> This very simple function does a very simple thing and I don't think I
>> need to explain it...
>
> The MAP function will iterate over any sequence, but you have to specify
> the output sequence type.
>
>   ;; iterate over list, results into vector
>   (map 'vector #'- '(1 2 3)) -> #(-1 -2 -3)
>
>   ;; iterate over vector, results into vector
>   (map 'vector #'- #(1 2 3)) -> #(-1 -2 -3)
>
>   ;; iterate over string, results into string
>   (map 'string #'char-upcase "abc") -> "ABC"
>
> If you want the resulting sequence to be the same as the incoming sequence,
> try this:
>
>  (defun inverse (vec)
>    (map (type-of vec) #'- vec))

This is what Joshua showed me in his follow up.

>> Curiously, if I throw it a (lisp) vector instead of a list argument,
>> there is a type error. Granted we all know lisp, so this is an
>> expected error.
>
> Not sure what you mean; the error is expected if you know that the loop
> for-as-in-list subclause (CLHS 6.1.2.1.2) clause takes only lists.

I think your confusion is a small bit of enlightenment for me. I think
I forgot that LOOP is a macro, and thus didn't think to expand it.

Lesson learned. Thank you! :)