From: Peter Scott
Subject: Common Lisp Utilities
Date: 
Message-ID: <1134597934.347442.112380@g49g2000cwa.googlegroups.com>
Several years ago, there was some talk about a semi-standard collection
of Common Lisp Utilities that everyone writes because they're useful
but not in the standard, like with-gensyms (or with-unique-names) and
collecting (or with-collectors). A bunch of these appeared on CLiki at
<http://www.cliki.net/Common%20Lisp%20Utilities>, some better written
and documented than others. The enthusiasm seems to have died down, but
a lot of good ideas were proposed while it lasted.

I collected these into a single project, cl-utilities
<http://common-lisp.net/project/cl-utilities/>, and put a lot of work
into good implementations and decent documentation. What I have is
stable now, and I'm looking for more utilities that people want.

So, my esteemed lispers: what other common lisp utilities would you
like to see? You can look at what I have in the manual
<http://common-lisp.net/project/cl-utilities/doc/>; what's it missing?
What utilities do you often use? I don't need detailed specs; a
wishlist would be just fine.

-Peter

From: ····@unreal.uncom
Subject: Re: Common Lisp Utilities
Date: 
Message-ID: <sb42q1pa1lp9337baapbl2d3vfecemdmfv@4ax.com>
On 14 Dec 2005 14:05:34 -0800, "Peter Scott" <·········@gmail.com>
wrote:

>What utilities do you often use? I don't need detailed specs; a
>wishlist would be just fine.

A combination of let* and labels, such that the variable and function
bindings can all reference each other.
From: Peter Scott
Subject: Re: Common Lisp Utilities
Date: 
Message-ID: <1134789431.054517.319000@g44g2000cwa.googlegroups.com>
Is a combination of let* and labels even possible in Common Lisp? It's
pretty clear what it would look like, but I can't figure out how I
would go about implementing it.

-Peter
From: ····@unreal.uncom
Subject: Re: Common Lisp Utilities
Date: 
Message-ID: <0277q1hkp3koco29q9glt87gnm55q5g7ep@4ax.com>
On 16 Dec 2005 19:17:11 -0800, "Peter Scott" <·········@gmail.com>
wrote:

>Is a combination of let* and labels even possible in Common Lisp? It's
>pretty clear what it would look like, but I can't figure out how I
>would go about implementing it.

Behind the scenes, in the macro expansion, there is a let which binds
all the variables to nil.  In the scope of that let there is a labels
which gives all the functions access to all the variables and to each
other.  In the scope of that labels there is a sequence of setq's
which initializes all the variables in sequence, as in let*.  Those
initializations have access to all the functions and all the
variables.

When you said "it's pretty clear what it would look like", what did
you have in mind?
From: Brian Downing
Subject: Re: Common Lisp Utilities
Date: 
Message-ID: <4YOof.651939$xm3.166499@attbi_s21>
In article <··································@4ax.com>,
 <····@unreal.uncom> wrote:
> On 16 Dec 2005 19:17:11 -0800, "Peter Scott" <·········@gmail.com> wrote:
> >Is a combination of let* and labels even possible in Common Lisp? It's
> >pretty clear what it would look like, but I can't figure out how I
> >would go about implementing it.
> 
> Behind the scenes, in the macro expansion, there is a let which binds
> all the variables to nil.  In the scope of that let there is a labels
> which gives all the functions access to all the variables and to each
> other.  In the scope of that labels there is a sequence of setq's
> which initializes all the variables in sequence, as in let*.  Those
> initializations have access to all the functions and all the
> variables.

This is a third interpretation in addition to the two that I presented
in my sibling post.  In fact, it's the first one I came up with, but I
discarded it because it would make:

(let ((x 4))
  (superbind ((y x)
              (x 42))
    (list x y)))
=> (42 NIL)

behave differently from:

(let ((x 4))
  (let* ((y x)
         (x 42))
    (list x y)))
=> (42 4)

which kind of violates the idea that it is a combination of LET* and
LABELS.

It just goes to show that there are way too many interpretations of how
an evil construct like this could work, though.

-bcd
-- 
*** Brian Downing <bdowning at lavos dot net> 
From: Brian Downing
Subject: Re: Common Lisp Utilities
Date: 
Message-ID: <CROof.651931$xm3.514689@attbi_s21>
In article <························@g44g2000cwa.googlegroups.com>,
Peter Scott <·········@gmail.com> wrote:
> Is a combination of let* and labels even possible in Common Lisp? It's
> pretty clear what it would look like, but I can't figure out how I
> would go about implementing it.

Sure, but you'd have to specify exactly what you want.  What should

(let ((x 4))
  (superbind ((x (foo))
              (foo ()
                (* x x)))
    (list x (foo))))

return?

Basically you'd have to choose whether the scope of X in the FOO
function is more "dynamic" or "lexical".  If it's dynamic, the first
call would see the outer X, while the second call would see the inner.
This is implementable in a couple of ways, but the obvious (and pretty
disgusting) way to do it is:

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defun superbind-transform (variable-bindings function-bindings body)
    `(labels ,function-bindings
       ,(if variable-bindings
            `(let (,(first variable-bindings))
               ,(superbind-transform (rest variable-bindings)
                                     function-bindings
                                     body))
            `(progn ,@body)))))

(defmacro superbind ((&rest bindings) &body body)
  (labels ((variable-binding (binding)
             (cond ((symbolp binding) (list (list binding nil)))
                   ((null (cddr binding)) (list binding))))
           (function-binding (binding)
             (when (and (consp binding) (cddr binding))
               (list binding))))
    (superbind-transform (mapcan #'variable-binding bindings)
                         (mapcan #'function-binding bindings)
                         body)))

CL-USER> (let ((x 4))
           (superbind ((x (foo))
                       (foo ()
                            (* x x)))
             (list x (foo))))
(16 256)

CL-USER> (macroexpand-1 `(superbind ((x (foo))
                                     (foo ()
                                       (* x x)))
                           (list x (foo))))
(LABELS ((FOO ()
           (* X X)))
  (LET ((X (FOO)))
    (LABELS ((FOO ()
               (* X X)))
      (PROGN (LIST X (FOO))))))

Viva code duplication!

If you're going for a strictly lexical approach, the FOO function would
see the X bound in the superbind, but when FOO is called from the X
initialization, X is still unbound, so that would be an error.  Doing
this with "real" lexicals would mean diving into the implementation
AFAIK, but fortunately we can use the value slot of uninterned symbols
with a SYMBOL-MACROLET to get a lexical scope:

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defun superbind-transform (variable-bindings function-bindings body)
    (let ((variable-bindings
           (mapcar (lambda (binding)
                     (append binding
                             (list (gensym (symbol-name (first binding)))
                                   (list 'make-symbol
                                         (symbol-name (first binding))))))
                   variable-bindings)))
      (flet ((select (&rest functions)
               (lambda (list)
                 (mapcar (lambda (fn) (funcall fn list)) functions))))
        `(let ,(mapcar (select #'third #'fourth) variable-bindings)
           (symbol-macrolet
               ,(mapcar (select #'first
                                (lambda (x) `(symbol-value ,(third x))))
                        variable-bindings)
             (labels ,function-bindings
               (setf ,@(mapcan (select #'first #'second) variable-bindings))
               ,@body)))))))

CL-USER> (let ((x 4))
           (superbind ((x (foo))
                       (foo ()
                         (* x x)))
             (list x (foo))))
; in: LAMBDA NIL
;     (LET ((X 4))
;     (SUPERBIND ((X (FOO)) (FOO NIL (* X X))) (LIST X (FOO))))
; 
; caught STYLE-WARNING:
;   The variable X is defined but never used.
; 
; compilation unit finished
;   caught 1 STYLE-WARNING condition
The variable #:X is unbound.
   [Condition of type UNBOUND-VARIABLE]

CL-USER> (macroexpand-1 `(superbind ((x (foo))
                                     (foo ()
                                       (* x x)))
                           (list x (foo))))
(LET ((#:X4552 (MAKE-SYMBOL "X")))
  (SYMBOL-MACROLET ((X (SYMBOL-VALUE #:X4552)))
    (LABELS ((FOO ()
               (* X X)))
      (SETF X (FOO))
      (LIST X (FOO)))))

I'm sure there are other "reasonable" interpretations as well.

This exercise has proved, at least to me, that this construct is a
really bad idea.  There are, IMHO, no obviously correct semantics for
what should happen when things start to get mixed up.

-bcd
-- 
*** Brian Downing <bdowning at lavos dot net> 
From: tichy
Subject: Re: Common Lisp Utilities
Date: 
Message-ID: <dnvd7r$gal$1@nemesis.news.tpi.pl>
Peter Scott wrote:
[ ..... ]
> What utilities do you often use? I don't need detailed specs; a
> wishlist would be just fine.

Hi.

I sometimes use GROUP, but it is probably too trivial to be
included in cl-utilities...

group (type-1 type-2 sequence predicate &rest more-predicates)

for example:

(group 'list 'list '(1 a "foo" 3 6 c #\Space) #'numberp #'symbolp)

=1=> ((1 3 6) (A C))
=2=> ("foo" #\Space)

another example (this functionality is not implemented in my code) :

(group 'list
        '(and vector (satisfies array-has-fill-pointer-p) (satisfies adjustable-array-p))
        '(1 a "foo" 3 6 c #\Space)
        #'numberp
        #'symbolp)

=1=> (#(1 3 6) #(A C))
=2=> ("foo" #\Space)

#(1 3 6) and #(A C) are adjustable arrays with fill-pointer.



--- another idea: GROUP-CAT

group-cat (result-type function sequence :key (test #'eql) return-keys)

result-type: (or alist plist hash-table)

example:

(group-cat 'plist (compose #'class-name #'class-of) '(1 a "foo" 3 6 c #\Space) :return-keys t)

=1=> (FIXNUM (1 3 6) SYMBOL (A C) SB-KERNEL::SIMPLE-CHARACTER-STRING ("foo") CHARACTER (#\Space))
=2=> (FIXNUM SYMBOL SB-KERNEL::SIMPLE-CHARACTER-STRING CHARACTER)

Regards, Szymon.
From: Coby Beck
Subject: Re: Common Lisp Utilities
Date: 
Message-ID: <ShHof.6064$ic1.3349@edtnps90>
"tichy" <···········@o2.pl> wrote in message 
·················@nemesis.news.tpi.pl...
>
> Hi.
>
> I sometimes use GROUP, but it is probably too trivial to be
> included in cl-utilities...
>
> group (type-1 type-2 sequence predicate &rest more-predicates)
>
> for example:
>
> (group 'list 'list '(1 a "foo" 3 6 c #\Space) #'numberp #'symbolp)
>
> =1=> ((1 3 6) (A C))
> =2=> ("foo" #\Space)

Nice, but it's too cryptic.  I think this is one of those places where loop, 
wordy as it is, provides the clearest way of expressing this.  You also are 
not the constrained to an implementation that returns only multiple values.

Of course you get to choose return types without special code.

> another example (this functionality is not implemented in my code) :
>
> (group 'list
>        '(and vector (satisfies array-has-fill-pointer-p) (satisfies 
> adjustable-array-p))
>        '(1 a "foo" 3 6 c #\Space)
>        #'numberp
>        #'symbolp)
>
> =1=> (#(1 3 6) #(A C))
> =2=> ("foo" #\Space)
>
> #(1 3 6) and #(A C) are adjustable arrays with fill-pointer.

Are you limited to getting two and only two groups?

-- 
Coby Beck
(remove #\Space "coby 101 @ bigpond . com")
From: Peter Scott
Subject: Re: Common Lisp Utilities
Date: 
Message-ID: <1134788427.436539.254010@g14g2000cwa.googlegroups.com>
Coby Beck wrote:
> Are you limited to getting two and only two groups?

It appears so, since only two return types can be specified. And I
can't think of a way around this problem that really appeals to me.

-Peter
From: tichy
Subject: Re: Common Lisp Utilities
Date: 
Message-ID: <do0gnp$nrp$1@atlantis.news.tpi.pl>
Peter Scott wrote:
> Coby Beck wrote:
>> Are you limited to getting two and only two groups?
> 
> It appears so, since only two return types can be specified.

No.

group (type-1 type-2 sequence predicate &rest more-predicates)
                                         ^^^^^^^^^^^^^^^^^^^^^
                                        -----------------------

type-1 is for level-0 sequence, type-2 (if symbol) is for all level-1 sequences.

(group 'vector 'list '(:x 1 "foo" #(10)) #'keywordp #'stringp #'integerp)

=1=> #((:x) (1) ("foo"))
=2=> #(#(10))


(group 'vector '(list vector) '(:x 1 "foo" #(10)) #'keywordp #'stringp #'integerp)

=1=> #((:x) #(1) ("foo"))
=2=> #(#(10))

Regards, Szymon.
From: tichy
Subject: Re: Common Lisp Utilities
Date: 
Message-ID: <do0ipr$ha3$1@nemesis.news.tpi.pl>
tichy wrote:

> (group 'vector '(list vector) '(:x 1 "foo" #(10)) #'keywordp #'stringp 
> #'integerp)
> 
> =1=> #((:x) #(1) ("foo"))
> =2=> #(#(10))

I just realized it's a bad idea, '(list vector) is not a valid type specifier,
it's ok, but may be too confusing for the user...

Regards, Szymon.
From: tichy
Subject: Re: Common Lisp Utilities
Date: 
Message-ID: <do0j3u$1ae$1@atlantis.news.tpi.pl>
tichy wrote:

> (group 'vector 'list '(:x 1 "foo" #(10)) #'keywordp #'stringp #'integerp)
> 
> =1=> #((:x) (1) ("foo"))
> =2=> #(#(10))

should be:
=1=> #((:x) ("foo") (1))
(I did not run REPL for this... I'm poor lisp implementation ;)

Regards, Szymon.
From: tichy
Subject: Re: Common Lisp Utilities
Date: 
Message-ID: <doriej$hc1$1@nemesis.news.tpi.pl>
Peter Scott wrote:
> Coby Beck wrote:
>> Are you limited to getting two and only two groups?
> 
> It appears so, since only two return types can be specified. And I
> can't think of a way around this problem that really appeals to me.

There is very similar function (SPLIT-IF) in SERIES 'package':

CL-USER> (split-if #Z("froboz" #\f 1 A 10 (x . y) #\x) #'numberp #'symbolp #'characterp)
#Z(1 10)
#Z(A)
#Z(#\f #\x)
#Z("froboz" (X . Y))

"...
SPLIT ITEMS &rest TEST-SERIES-INPUTS                             [Function]
SPLIT-IF ITEMS &rest TEST-PREDICATES                             [Function]

These functions are like CHOOSE and CHOOSE-IF except that instead of
producing one restricted output, they partition the input series ITEMS
between several outputs.  If there are N test inputs following ITEMS, then
there are N+1 outputs.  Each input element is placed in exactly one output
series, depending on the outcome of a sequence of tests.  If the element
ITEMS[j] fails the first K-1 tests and passes the kth test, it is put in
the kth output.  If ITEMS[j] fails every test, it is placed in the last
output.  In addition, all output stops as soon as any series input runs out
of elements.  The test inputs to SPLIT are series of values; ITEMS[j]
passes the kth test if the jth element of the kth test-series is not NIL.
The test inputs to SPLIT-IF are predicates; ITEMS[j] passes the kth test if
the kth test predicate returns non-null when applied to ITEMS[j]."
From: Tin Gherdanarra
Subject: Re: Common Lisp Utilities
Date: 
Message-ID: <40naeeF1b9a7fU1@individual.net>
You could ask Paul Graham to lift the utilities
he has published in his seminal book /On Lisp/.
There are dozens of very useful smallish utilities.

http://www.paulgraham.com/onlisp.html

There is another project that overlaps with yours
called "The Common Lisp Cookbook". Maybe you guys
can join forces.

http://cl-cookbook.sourceforge.net/
From: Paolo Amoroso
Subject: Re: Common Lisp Utilities
Date: 
Message-ID: <87y82hnofk.fsf@plato.moon.paoloamoroso.it>
Tin Gherdanarra <···········@gmail.com> writes:

> You could ask Paul Graham to lift the utilities
> he has published in his seminal book /On Lisp/.

See directory src/onlisp:

  http://cvs.sourceforge.net/viewcvs.py/clocc/clocc/src/onlisp/

of CLOCC:

  http://clocc.sourceforge.net


Paolo
-- 
Why Lisp? http://wiki.alu.org/RtL%20Highlight%20Film
Recommended Common Lisp libraries/tools:
- ASDF/ASDF-INSTALL: system building/installation
- CL-PPCRE: regular expressions
- CFFI: Foreign Function Interface