From: rif
Subject: Need some combination of case and cond
Date: 
Message-ID: <wj0lljrhtld.fsf@five-percent-nation.mit.edu>
Is there a cleaner/better way to say this in CL?

(defconstant *foo* 1)
(defconstant *bar* 2)
(defconstant *quux* 3)

(defun dispatch (state)
   (cond ((= state *foo*) 'foo)
         ((= state *bar*) 'bar)
         ((= state *quux*) 'quux)
         (t 'unknown)))

I find this distasteful because of the repetitiveness of the "= state"
clauses (this is showing up a number of times in my code).  I tried to
use case instead of cond, but this didn't work, because (afaict) the
keys in case are not evaluated; I ended up with a function that wanted
to be passed the symbols '*foo* and '*bar* rather than their values.

It should be easy to write a macro on top of cond (or on top of case,
if I want to restrict myself to compile-time known values), but this
seems like the sort of thing there must be some standard way to do
that I just don't know about?

Cheers,

rif

From: Kenny Tilton
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <Ll6qc.128209$WA4.17756@twister.nyc.rr.com>
rif wrote:

> Is there a cleaner/better way to say this in CL?
> 
> (defconstant *foo* 1)
> (defconstant *bar* 2)
> (defconstant *quux* 3)
> 
> (defun dispatch (state)
>    (cond ((= state *foo*) 'foo)
>          ((= state *bar*) 'bar)
>          ((= state *quux*) 'quux)
>          (t 'unknown)))

Try (case state (#.*foo* 'foo)....

kenny

-- 
Home? http://tilton-technology.com
Cells? http://www.common-lisp.net/project/cells/
Cello? http://www.common-lisp.net/project/cello/
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
Your Project Here! http://alu.cliki.net/Industry%20Application
From: rif
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <wj0lljr3plp.fsf@five-percent-nation.mit.edu>
Kenny Tilton <·······@nyc.rr.com> writes:

> rif wrote:
> 
> > Is there a cleaner/better way to say this in CL?
> > (defconstant *foo* 1)
> > (defconstant *bar* 2)
> > (defconstant *quux* 3)
> > (defun dispatch (state)
> >    (cond ((= state *foo*) 'foo)
> >          ((= state *bar*) 'bar)
> >          ((= state *quux*) 'quux)
> >          (t 'unknown)))
> 
> Try (case state (#.*foo* 'foo)....
> 
> kenny

That seems to be just what I need.  Thanks!

rif
From: Pascal Costanza
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <c8aueg$po1$1@newsreader2.netcologne.de>
Kenny Tilton wrote:

> rif wrote:
> 
>> Is there a cleaner/better way to say this in CL?
>>
>> (defconstant *foo* 1)
>> (defconstant *bar* 2)
>> (defconstant *quux* 3)
>>
>> (defun dispatch (state)
>>    (cond ((= state *foo*) 'foo)
>>          ((= state *bar*) 'bar)
>>          ((= state *quux*) 'quux)
>>          (t 'unknown)))
> 
> Try (case state (#.*foo* 'foo)....

Sidenote: I think it's more common to use + instead of * for constants, 
so that would be +foo+, +bar+ and +quux+. Otherwise, I would expect to 
be able to rebind those variables dynamically.


Pascal

-- 
1st European Lisp and Scheme Workshop
June 13 - Oslo, Norway - co-located with ECOOP 2004
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
From: Steven E. Harris
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <q67hdue99kr.fsf@L75001820.us.ray.com>
Pascal Costanza <········@web.de> writes:

> Sidenote: I think it's more common to use + instead of * for
> constants, so that would be +foo+, +bar+ and +quux+.

Another naming style question: What are some common idioms for gensym
names in macros? I've seen prefixing with 'g', but I find that those
"gnames" don't stick out well enough in my code.

-- 
Steven E. Harris        :: ········@raytheon.com
Raytheon                :: http://www.raytheon.com
From: Pascal Costanza
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <c8b2uo$64v$1@newsreader2.netcologne.de>
Steven E. Harris wrote:

> Pascal Costanza <········@web.de> writes:
> 
>>Sidenote: I think it's more common to use + instead of * for
>>constants, so that would be +foo+, +bar+ and +quux+.
> 
> Another naming style question: What are some common idioms for gensym
> names in macros? I've seen prefixing with 'g', but I find that those
> "gnames" don't stick out well enough in my code.

Hm, you know that you can add a more descriptive by saying (gensym 
"MORE-DESCRIPTIVE-NAME") ?

Pascal

-- 
1st European Lisp and Scheme Workshop
June 13 - Oslo, Norway - co-located with ECOOP 2004
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
From: Steven E. Harris
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <q67vfiu7ro8.fsf@L75001820.us.ray.com>
Pascal Costanza <········@web.de> writes:

> Hm, you know that you can add a more descriptive by saying (gensym
> "MORE-DESCRIPTIVE-NAME") ?

Yes, and I do that. I'm talking about tagging the gensym variables so
that I can track them as distinct from the actual arguments to the
macro. Consider something like this:

(defmacro example (arg)
  (let ((garg (gensym "arg-")))
    `(let ((,garg ,arg))
      (when ,garg
        (format t "arg is ~A." ,garg)))))


It's the "garg" that bothers me. Is there some convention like ".arg."
or "^arg^" to denote gensym variables?

-- 
Steven E. Harris        :: ········@raytheon.com
Raytheon                :: http://www.raytheon.com
From: Pascal Costanza
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <c8b6od$e1i$1@newsreader2.netcologne.de>
Steven E. Harris wrote:
> Pascal Costanza <········@web.de> writes:
> 
>>Hm, you know that you can add a more descriptive by saying (gensym
>>"MORE-DESCRIPTIVE-NAME") ?
> 
> Yes, and I do that. I'm talking about tagging the gensym variables so
> that I can track them as distinct from the actual arguments to the
> macro. Consider something like this:
> 
> (defmacro example (arg)
>   (let ((garg (gensym "arg-")))
>     `(let ((,garg ,arg))
>       (when ,garg
>         (format t "arg is ~A." ,garg)))))
> 
> It's the "garg" that bothers me. Is there some convention like ".arg."
> or "^arg^" to denote gensym variables?

I don't know about any convention for those, and I don't care a lot. 
It's similar to local variables in functions, and you don't name them 
specially as well, do you?

I try to use with-unique-names/with-gensyms and rebinding to cover the 
important cases. Your code would look like this:

(defmacro example (arg)
   (rebinding (arg)
     `(when ,arg
        (format t "arg is ~A." ,arg))))

A definition for with-gensyms can be found in Paul Graham's On Lisp. 
with-unique-names and rebinding are part of the LispWorks library. I 
don't know of places where you can find a definition for rebinding, 
though, but it exists somewhere.


Pascal

-- 
1st European Lisp and Scheme Workshop
June 13 - Oslo, Norway - co-located with ECOOP 2004
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
From: Steven E. Harris
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <q67brkm7nwo.fsf@L75001820.us.ray.com>
Pascal Costanza <········@web.de> writes:

> I don't know about any convention for those, and I don't care a
> lot. It's similar to local variables in functions, and you don't
> name them specially as well, do you?

No, but I usually create local variables for a purpose distinct from
the contributing function arguments, and hence have some better naming
ideas in mind. These gensyms are there for one-to-one correspondence
with the function arguments. It's hard to call them something other
than the original name.

> (defmacro example (arg)
>    (rebinding (arg)
>      `(when ,arg
>         (format t "arg is ~A." ,arg))))

Very nice.

> with-unique-names and rebinding are part of the LispWorks library. I
> don't know of places where you can find a definition for rebinding,
> though, but it exists somewhere.

I just found the REBINDING and WITH-UNIQUE-NAME specification
discussions and the respective pages on CLiki. I also found them on my
disk in the CL-PPCRE library in the utils.lisp file. The versions
there are a little more refined than the reference implementations
offered on CLiki.

Now, this may be more fuel for the "standardization v. libraries"
thread from last week, but now I wonder: Should I start using the
(non-exported) versions present in CL-PPCRE, or copy those definitions
into my own file? Of course, I'd really like to ASDF-INSTALL some
small utility library that contains these two functions. There's some
discussion of an actual "Common Lisp Utilities" package�. That
actually existing somewhere would be grand.


Footnotes: 
� http://www.cliki.net/Common%20Lisp%20Utilities

-- 
Steven E. Harris        :: ········@raytheon.com
Raytheon                :: http://www.raytheon.com
From: Svein Ove Aas
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <Jjaqc.2103$RL3.55810@news2.e.nsc.no>
Steven E. Harris wrote:

> Now, this may be more fuel for the "standardization v. libraries"
> thread from last week, but now I wonder: Should I start using the
> (non-exported) versions present in CL-PPCRE, or copy those definitions
> into my own file? Of course, I'd really like to ASDF-INSTALL some
> small utility library that contains these two functions. There's some
> discussion of an actual "Common Lisp Utilities" package�. That
> actually existing somewhere would be grand.

Why not go ahead and make it, then?

I'd use it, so that's an audience of two already.
From: Steven E. Harris
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <q67y8nq6565.fsf@L75001820.us.ray.com>
Svein Ove Aas <··············@brage.info> writes:

> Why not go ahead and make it, then?

Well, I'll give it a shot, using something small like SPLIT-SEQUENCE
as a guide.

-- 
Steven E. Harris        :: ········@raytheon.com
Raytheon                :: http://www.raytheon.com
From: Pascal Costanza
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <c8bbn7$pbp$1@newsreader2.netcologne.de>
Steven E. Harris wrote:

> Now, this may be more fuel for the "standardization v. libraries"
> thread from last week, but now I wonder: Should I start using the
> (non-exported) versions present in CL-PPCRE, or copy those definitions
> into my own file? Of course, I'd really like to ASDF-INSTALL some
> small utility library that contains these two functions. There's some
> discussion of an actual "Common Lisp Utilities" package�. That
> actually existing somewhere would be grand.

I find the lispworks package in LispWorks pretty handy, and I think some 
kind of lw-compat would be a good idea. I have started to implement some 
of the stuff in it, but found it too tiresome in the end.


Pascal

-- 
1st European Lisp and Scheme Workshop
June 13 - Oslo, Norway - co-located with ECOOP 2004
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
From: rif
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <wj0pt93sz87.fsf@five-percent-nation.mit.edu>
Pascal Costanza <········@web.de> writes:

> Kenny Tilton wrote:
> 
> > rif wrote:
> >
> >> Is there a cleaner/better way to say this in CL?
> >>
> >> (defconstant *foo* 1)
> >> (defconstant *bar* 2)
> >> (defconstant *quux* 3)
> >>
> >> (defun dispatch (state)
> >>    (cond ((= state *foo*) 'foo)
> >>          ((= state *bar*) 'bar)
> >>          ((= state *quux*) 'quux)
> >>          (t 'unknown)))
> > Try (case state (#.*foo* 'foo)....
> 
> Sidenote: I think it's more common to use + instead of * for
> constants, so that would be +foo+, +bar+ and +quux+. Otherwise, I
> would expect to be able to rebind those variables dynamically.
> 
> 
> Pascal

Thanks!

Is there a list of these conventions?  Right now, I am using + marks
to surround symbols denoting functions that are actually foreign
functions.  I could certainly use +'s for both of these things, but is
there a common way to notate foreign-functions?

rif
From: Erann Gat
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <gNOSPAMat-562326.12035617052004@nntp1.jpl.nasa.gov>
In article <···············@five-percent-nation.mit.edu>,
 rif <···@mit.edu> wrote:

> Pascal Costanza <········@web.de> writes:
> 
> > Kenny Tilton wrote:
> > 
> > > rif wrote:
> > >
> > >> Is there a cleaner/better way to say this in CL?
> > >>
> > >> (defconstant *foo* 1)
> > >> (defconstant *bar* 2)
> > >> (defconstant *quux* 3)
> > >>
> > >> (defun dispatch (state)
> > >>    (cond ((= state *foo*) 'foo)
> > >>          ((= state *bar*) 'bar)
> > >>          ((= state *quux*) 'quux)
> > >>          (t 'unknown)))
> > > Try (case state (#.*foo* 'foo)....
> > 
> > Sidenote: I think it's more common to use + instead of * for
> > constants, so that would be +foo+, +bar+ and +quux+. Otherwise, I
> > would expect to be able to rebind those variables dynamically.
> > 
> > 
> > Pascal
> 
> Thanks!
> 
> Is there a list of these conventions?  Right now, I am using + marks
> to surround symbols denoting functions that are actually foreign
> functions.  I could certainly use +'s for both of these things, but is
> there a common way to notate foreign-functions?

The usual convention for foreign functions and other "dangerous" things 
is to precede them with a % sign.  Sometimes two percent signs are used 
to denote especially dangerous things.

BTW, since your state identifiers are a contiguous sequence of integers 
you might want to consider:

(defconstant +states+ '#(foo bar quux))

(defun dispatch (state)
  (if (<= 1 state (length +states+))
    (elt +states+ (1- state))
    'unknown))

Not only is it less code, but it's faster too.

E.
From: rif
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <wj01xli690m.fsf@five-percent-nation.mit.edu>
> The usual convention for foreign functions and other "dangerous" things 
> is to precede them with a % sign.  Sometimes two percent signs are used 
> to denote especially dangerous things.

Ah, thanks much!

Cheers,

rif

ps.  The "contiguous sequence of integers" was just an example.  In
practice, they end up not being contiguous.  But your point is
well-taken.
From: Erann Gat
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <gNOSPAMat-EDDC90.14395117052004@nntp1.jpl.nasa.gov>
In article <···············@five-percent-nation.mit.edu>,
 rif <···@mit.edu> wrote:

> ps.  The "contiguous sequence of integers" was just an example.  In
> practice, they end up not being contiguous.

They don't have to be contiguous, just dense.  And if they aren't dense 
then using a hash table (or an alist or some other kind of associative 
map) instead of a vector would work.

Implementing a lookup table as a bunch of constants and case/cond 
statements is one of my pet peeves.  Implementing a lookup table as an 
associative map is both easier and faster.  There's no reason not to do 
it that way.

E.
From: Joe Marshall
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <3c5xkagn.fsf@ccs.neu.edu>
Erann Gat <·········@flownet.com> writes:

> They don't have to be contiguous, just dense.  And if they aren't dense 
> then using a hash table (or an alist or some other kind of associative 
> map) instead of a vector would work.
>
> Implementing a lookup table as a bunch of constants and case/cond 
> statements is one of my pet peeves.  Implementing a lookup table as an 
> associative map is both easier and faster.  There's no reason not to do 
> it that way.

I needed to generate a mask based on certain bit patterns in a
fixnum.  Although the set is dense, it was too big to put in a table
or alist, but because the masks applied to huge swaths of the fixnums
it was possible to implement the lookup as a bunch of constants and
nested cond statements:

(defun compute-mask (num)
  (declare (type num fixnum))
  (if (> num #x0FFE00)
      (if (> num #x0FFFE0)
          (if (> num #x0FFFFE)
              +mask-0+
              +mask-1+)
          (if (> num #x0FFF00)
              +mask-2+
              +mask-3+)
   ... etc. etc. etc. ...

This gives you the answer in about log time and the compiled code is
very efficient.

Of course the real reason I did it was to irritate Erann.
From: Erann Gat
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <gNOSPAMat-654391.09404418052004@nntp1.jpl.nasa.gov>
In article <············@ccs.neu.edu>, Joe Marshall <···@ccs.neu.edu> 
wrote:

> Erann Gat <·········@flownet.com> writes:
> 
> > They don't have to be contiguous, just dense.  And if they aren't dense 
> > then using a hash table (or an alist or some other kind of associative 
> > map) instead of a vector would work.
> >
> > Implementing a lookup table as a bunch of constants and case/cond 
> > statements is one of my pet peeves.  Implementing a lookup table as an 
> > associative map is both easier and faster.  There's no reason not to do 
> > it that way.
> 
> I needed to generate a mask based on certain bit patterns in a
> fixnum.  Although the set is dense, it was too big to put in a table
> or alist, but because the masks applied to huge swaths of the fixnums
> it was possible to implement the lookup as a bunch of constants and
> nested cond statements:
> 
> (defun compute-mask (num)
>   (declare (type num fixnum))
>   (if (> num #x0FFE00)
>       (if (> num #x0FFFE0)
>           (if (> num #x0FFFFE)
>               +mask-0+
>               +mask-1+)
>           (if (> num #x0FFF00)
>               +mask-2+
>               +mask-3+)
>    ... etc. etc. etc. ...
> 
> This gives you the answer in about log time and the compiled code is
> very efficient.
>
> Of course the real reason I did it was to irritate Erann.

Heh, and I thought it was to demonstrate your ignorance of binary search 
trees.  <shrug>  Shows how much I know.

E.
From: Joe Marshall
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <1xlhiifn.fsf@ccs.neu.edu>
Erann Gat <·········@flownet.com> writes:

> In article <············@ccs.neu.edu>, Joe Marshall <···@ccs.neu.edu> 
> wrote:
>
>>
>> Of course the real reason I did it was to irritate Erann.
>
> Heh, and I thought it was to demonstrate your ignorance of binary search 
> trees.

I usually demonstrate my ignorance in much more spectacular ways, but
I felt I should make an exception.
From: Carl Shapiro
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <ouyoeomvde0.fsf@panix3.panix.com>
Erann Gat <·········@flownet.com> writes:

> The usual convention for foreign functions and other "dangerous" things 
> is to precede them with a % sign.  Sometimes two percent signs are used 
> to denote especially dangerous things.

Weird.  Over on my half of the universe, the single percent sign
denotes a function which could create erroneous pointers whereas two
percent signs are reserved to denote sub-primitives.

Interlisp used decorate the symbol names of functions which performed
no argument checking with a preceding 'N'.  LispWorks does something
similar with their FUNCTION$TYPE1$TYPE2$..$TYPEN names.
From: Erann Gat
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <gNOSPAMat-9C9476.22204417052004@nntp1.jpl.nasa.gov>
In article <···············@panix3.panix.com>,
 Carl Shapiro <·············@panix.com> wrote:

> Erann Gat <·········@flownet.com> writes:
> 
> > The usual convention for foreign functions and other "dangerous" things 
> > is to precede them with a % sign.  Sometimes two percent signs are used 
> > to denote especially dangerous things.
> 
> Weird.  Over on my half of the universe, the single percent sign
> denotes a function which could create erroneous pointers

Yes, that's what I meant by "dangerous".  I've seen the convention of 
%-prefix means something low-level (and hence dangerous) used in many 
Lisps, including pre-CL Lisps.

> whereas two percent signs are reserved to denote sub-primitives.

Here I have fewer data points.  MCL uses a $ prefix for subprimitives.  
It also has some functions with double-% prefixes, but I can't quite 
figure out a particular pattern.  I just assumed that if one % means 
"dangerous" then two %'s mean "especially dangerous."

> Interlisp used decorate the symbol names of functions which performed
> no argument checking with a preceding 'N'.

In Common Lisp, an N prefix generally denotes a destructive (but not 
otherwise hazardous) function.

> LispWorks does something
> similar with their FUNCTION$TYPE1$TYPE2$..$TYPEN names.

That seems like a good idea for C++, but not otherwise IMHO.

E.
From: Carl Shapiro
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <ouyr7tiqjxn.fsf@panix3.panix.com>
Erann Gat <·········@flownet.com> writes:

> > whereas two percent signs are reserved to denote sub-primitives.
> 
> Here I have fewer data points.  MCL uses a $ prefix for subprimitives.  
> It also has some functions with double-% prefixes, but I can't quite 
> figure out a particular pattern.  I just assumed that if one % means 
> "dangerous" then two %'s mean "especially dangerous."

It sounds like some people got carried away.

> In Common Lisp, an N prefix generally denotes a destructive (but not 
> otherwise hazardous) function.

The D prefix in Interlisp denoted destructive functions.  The N prefix
in Common Lisp seems to be an artifact of history.  The NCONC function
in Lisp 1.5 was a dyadic function, whereas the sundry CONC function was
variadic.  Both were destructive.  Go figure.

Interlisp was somewhat more consistent when it came to naming symbols.
SET, SETQ, and SETQQ all did exactly what they looked like they would
do.

> > LispWorks does something
> > similar with their FUNCTION$TYPE1$TYPE2$..$TYPEN names.
> 
> That seems like a good idea for C++, but not otherwise IMHO.

Just as a C++ user will never enter name-mangled symbol names into
their program code, in all likelihood a LispWorks user will never find
themselves calling these primitives directly.

However, let's not lose sight of the real issue here: most Lisp
systems end up using wildly inconsistent naming schemes for their
primitive or non-checked functions.  I fail to see how the $-delimited
nomenclature can be considered any worse than the (underspecified and
unintuitive) alternatives.
From: Björn Lindberg
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <hcsd652zu0s.fsf@knatte.nada.kth.se>
rif <···@mit.edu> writes:

> Pascal Costanza <········@web.de> writes:
> 
> > Kenny Tilton wrote:
> > 
> > > rif wrote:
> > >
> > >> Is there a cleaner/better way to say this in CL?
> > >>
> > >> (defconstant *foo* 1)
> > >> (defconstant *bar* 2)
> > >> (defconstant *quux* 3)
> > >>
> > >> (defun dispatch (state)
> > >>    (cond ((= state *foo*) 'foo)
> > >>          ((= state *bar*) 'bar)
> > >>          ((= state *quux*) 'quux)
> > >>          (t 'unknown)))
> > > Try (case state (#.*foo* 'foo)....
> > 
> > Sidenote: I think it's more common to use + instead of * for
> > constants, so that would be +foo+, +bar+ and +quux+. Otherwise, I
> > would expect to be able to rebind those variables dynamically.
> > 
> > 
> > Pascal
> 
> Thanks!
> 
> Is there a list of these conventions?  Right now, I am using + marks
> to surround symbols denoting functions that are actually foreign
> functions.  I could certainly use +'s for both of these things, but is
> there a common way to notate foreign-functions?

  http://www.cliki.net/Naming%20conventions

There doesn't seem to be any convention for foreign functions, but on
the other hand, why would you want that?


Bj�rn
From: Harald Hanche-Olsen
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <pcor7tihq6y.fsf@shuttle.math.ntnu.no>
+ Kenny Tilton <·······@nyc.rr.com>:

| rif wrote:
| 
| > Is there a cleaner/better way to say this in CL?
| > (defconstant *foo* 1)
| > (defconstant *bar* 2)
| > (defconstant *quux* 3)
| > (defun dispatch (state)
| >    (cond ((= state *foo*) 'foo)
| >          ((= state *bar*) 'bar)
| >          ((= state *quux*) 'quux)
| >          (t 'unknown)))
| 
| Try (case state (#.*foo* 'foo)....

What happens when the file is compiled?  Is there a need to wrap
eval-when forms around the defconstant forms?

-- 
* Harald Hanche-Olsen     <URL:http://www.math.ntnu.no/~hanche/>
- Debating gives most of us much more psychological satisfaction
  than thinking does: but it deprives us of whatever chance there is
  of getting closer to the truth.  -- C.P. Snow
From: Harald Hanche-Olsen
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <pcon046hpd1.fsf@shuttle.math.ntnu.no>
+ Harald Hanche-Olsen <······@math.ntnu.no>:

| What happens when the file is compiled?  Is there a need to wrap
| eval-when forms around the defconstant forms?

Um, after some further reading of the DEFCONSTANT section in the CLHS
I guess not.  Sorry 'bout the noise.

-- 
* Harald Hanche-Olsen     <URL:http://www.math.ntnu.no/~hanche/>
- Debating gives most of us much more psychological satisfaction
  than thinking does: but it deprives us of whatever chance there is
  of getting closer to the truth.  -- C.P. Snow
From: Kenny Tilton
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <nd8qc.128372$WA4.79333@twister.nyc.rr.com>
Harald Hanche-Olsen wrote:

> + Harald Hanche-Olsen <······@math.ntnu.no>:
> 
> | What happens when the file is compiled?  Is there a need to wrap
> | eval-when forms around the defconstant forms?
> 
> Um, after some further reading of the DEFCONSTANT section in the CLHS
> I guess not.  Sorry 'bout the noise.

No, it is a good point. I do not fully understand these, but the value 
returned by the form after the #. read-time evaluation thingy has to be 
something the compiler can write out/read back in. It knows about 
numbers (this case), but some things require the user to specialize 
make-load-form on the thing. Pardon the hand-waving, this is a corner of 
CL I have yet to visit.

kenny

-- 
Home? http://tilton-technology.com
Cells? http://www.common-lisp.net/project/cells/
Cello? http://www.common-lisp.net/project/cello/
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
Your Project Here! http://alu.cliki.net/Industry%20Application
From: Steven M. Haflich
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <pifqc.450$PH7.346@newssvr27.news.prodigy.com>
Harald Hanche-Olsen wrote:

> + Harald Hanche-Olsen <······@math.ntnu.no>:
> 
> | What happens when the file is compiled?  Is there a need to wrap
> | eval-when forms around the defconstant forms?
> 
> Um, after some further reading of the DEFCONSTANT section in the CLHS
> I guess not.  Sorry 'bout the noise.

I'm glad that, after all this noise about naming of gensyms etc., someone
has resturned to the initial queation.  But I don't agree with the above
conclusion.

| > (defconstant *foo* 1)
| > (defconstant *bar* 2)
| > (defconstant *quux* 3)
| > (defun dispatch (state)
| >    (cond ((= state *foo*) 'foo)
| >          ((= state *bar*) 'bar)
| >          ((= state *quux*) 'quux)
| >          (t 'unknown)))
|
| Try (case state (#.*foo* 'foo)....

I can find no justification in the ANS that "#.foo" will be read as
obviously intended.  Even ignoring the issue of *read-eval*, the ANS
says:

  If a defconstant form appears as a top level form, the compiler must
  recognize that name names a constant variable. An implementation may
  choose to evaluate the value-form at compile time, load time, or both.

If the implementation chooses _not_ to evaluate the value form, that
value form can hardly be available to the reader.  Even if the
implementation +does+ choose to evaluate the value form at compile
time, I can still find no proof within the ANS that the _reader_ will
have that definition available to it.

Even if it works in every extant implementation, I conclude that this
code is not portable CL.
From: Rob Warnock
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <EtmdnQsRh-rxxzbdRVn-gQ@speakeasy.net>
Steven M. Haflich <·················@alum.mit.edu> wrote:
+---------------
| Harald Hanche-Olsen wrote:
| > | What happens when the file is compiled?  Is there a need to wrap
| > | eval-when forms around the defconstant forms?
| > 
| > Um, after some further reading of the DEFCONSTANT section in the CLHS
| > I guess not.  Sorry 'bout the noise.
| 
| I'm glad that, after all this noise about naming of gensyms etc., someone
| has resturned to the initial queation.  But I don't agree with the above
| conclusion.
...
| ...the ANS says:
| 
|   If a defconstant form appears as a top level form, the compiler must
|   recognize that name names a constant variable. An implementation may
|   choose to evaluate the value-form at compile time, load time, or both.
| 
| If the implementation chooses _not_ to evaluate the value form, that
| value form can hardly be available to the reader.  Even if the
| implementation +does+ choose to evaluate the value form at compile
| time, I can still find no proof within the ANS that the _reader_ will
| have that definition available to it.
+---------------

And in fact, in CMUCL it will *NOT*!!

This led to an actual portability problem in Tim Bradshaw's HTOUT macro 
package a while back, which he & I hashed out at great length before
reaching the same conclusion you reach above. [ISTR he was using Lispworks,
while I was using CMUCL.] CMUCL obeys the letter of the ANS, but both
Tim & I agreed that the above-quoted section of the ANS makes CONSTANTP[1]
practically *useless* [or at least non-portable] at compile time for
doing any kind of constant folding, since it is possible in a conforming
implementation that CONSTANTP [at compile time, say, in a macro] might
return "T" for every sub-form of a "constant" expression, yet the value(s)
of some sub-form(s) might not be available at compile time. (*sigh*)


-Rob

[1] The CLHS says:

	...symbols declared as constant by the user in the indicated
	environment using DEFCONSTANT are always considered constant forms
	and must be recognized as such by CONSTANTP.

    But what is *NOT* required by DEFCONSTANT is that the *value* of a
    "symbol declared as constant by the user" be available at compile time!

-----
Rob Warnock			<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: Christophe Rhodes
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <sqvfisftu3.fsf@lambda.dyndns.org>
····@rpw3.org (Rob Warnock) writes:

> And in fact, in CMUCL it will *NOT*!!

Erm, are you sure?

·····@mu:~/misc-cvs/gcl/ansi-tests$ cat > /tmp/foo.lisp
(defconstant +foo+ 1)
(print #.+foo+)
·····@mu:~/misc-cvs/gcl/ansi-tests$ lisp
* (load (compile-file "/tmp/foo.lisp"))

1
T

I agree with the general uselessness of CONSTANTP as specified,
though.

Christophe
-- 
http://www-jcsu.jesus.cam.ac.uk/~csr21/       +44 1223 510 299/+44 7729 383 757
(set-pprint-dispatch 'number (lambda (s o) (declare (special b)) (format s b)))
(defvar b "~&Just another Lisp hacker~%")    (pprint #36rJesusCollegeCambridge)
From: Harald Hanche-Olsen
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <pco1xlf5q22.fsf@shuttle.math.ntnu.no>
+ Christophe Rhodes <·····@cam.ac.uk>:

| ····@rpw3.org (Rob Warnock) writes:
| 
| > And in fact, in CMUCL it will *NOT*!!
| 
| Erm, are you sure?
| 
| ·····@mu:~/misc-cvs/gcl/ansi-tests$ cat > /tmp/foo.lisp
| (defconstant +foo+ 1)
| (print #.+foo+)
| ·····@mu:~/misc-cvs/gcl/ansi-tests$ lisp
| * (load (compile-file "/tmp/foo.lisp"))
| 
| 1
| T

This may take us too far into CMUCL internals, but the state of +foo+
after compile-time handling of (defconstant +foo+ 1) is a bit weird.

CL-USER> (macroexpand-1 '(defconstant +const+ 1))
(PROGN
 (EVAL-WHEN (:COMPILE-TOPLEVEL)
   (C::DO-DEFCONSTANT-COMPILE-TIME '+CONST+ 1 'NIL))
 (EVAL-WHEN (:LOAD-TOPLEVEL :EXECUTE) (C::%%DEFCONSTANT '+CONST+ 1 'NIL)))
T

Here, C::DO-DEFCONSTANT-COMPILE-TIME is defined as follows (with error
detection code elided)

(defun do-defconstant-compile-time (name value doc)
  (setf (info variable kind name) :constant)
  (setf (info variable where-from name) :defined)
  (setf (info variable constant-value name) value)
  (remhash name *free-variables*))

So let's pretend to be the compiler for a moment:

CL-USER> (let ((c::*free-variables* (make-hash-table)))
	   (c::do-defconstant-compile-time '+foo+ 1 'nil))
  1
NIL
CL-USER> (boundp '+foo+)
NIL
CL-USER> +foo+
1

So evaluating +foo+ is fine, even though the symbol is unbound.

In fact, if you replace #.+foo+ by #.(symbol-value '+foo+) in your
experiment it will fail.

In the following incarnation, though, it works just fine.

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defconstant +foo+ 3))
(print #.(symbol-value '+foo+))

So in the end I was right about the need for EVAL-WHEN, albeit for the
wrong reason.

-- 
* Harald Hanche-Olsen     <URL:http://www.math.ntnu.no/~hanche/>
- Debating gives most of us much more psychological satisfaction
  than thinking does: but it deprives us of whatever chance there is
  of getting closer to the truth.  -- C.P. Snow
From: Kalle Olavi Niemitalo
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <87vfiugzl9.fsf@Astalo.kon.iki.fi>
Harald Hanche-Olsen <······@math.ntnu.no> writes:

> What happens when the file is compiled?  Is there a need to wrap
> eval-when forms around the defconstant forms?

Yes, there is.  From DEFCONSTANT: "An implementation may choose
to evaluate the value-form at compile time, load time, or both."
If the implementation chooses to evaluate the value-form at load
time only (and the constant has not been already defined for some
other reason), the value will not be available to #. at compile
time.  The compiler is required to note the existence of the
constant but not necessarily its value.

Also, if I understand sections 3.2.1 and 3.2.2.3 correctly, the
value of the constant might not be visible to #. even if the
implementation evaluates the value-form at compile time: the
result will be saved in the compilation environment, which may
be separate from the evaluation environment used by #..

Finally, if you do (progn (defconstant foo 42) #.foo), the value
of FOO will be needed before the DEFCONSTANT is even compiled.
This was not the case in Mr. Tilton's code, though.
From: Martin Raspaud
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <c8f9bn$a9p$1@news.u-bordeaux.fr>
Kenny Tilton wrote:
> 
> 
> rif wrote:
> 
>> Is there a cleaner/better way to say this in CL?
>>
>> (defconstant *foo* 1)
>> (defconstant *bar* 2)
>> (defconstant *quux* 3)
>>
>> (defun dispatch (state)
>>    (cond ((= state *foo*) 'foo)
>>          ((= state *bar*) 'bar)
>>          ((= state *quux*) 'quux)
>>          (t 'unknown)))
> 
> 
> Try (case state (#.*foo* 'foo)....
> 
> kenny
> 


Is there a way to do this with strings ? (ie specifying the equality 
test to perform) ?

Martin
From: Kenny Tilton
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <mQHqc.48912$mX.18042380@twister.nyc.rr.com>
Martin Raspaud wrote:

> Kenny Tilton wrote:
> 
>>
>>
>> rif wrote:
>>
>>> Is there a cleaner/better way to say this in CL?
>>>
>>> (defconstant *foo* 1)
>>> (defconstant *bar* 2)
>>> (defconstant *quux* 3)
>>>
>>> (defun dispatch (state)
>>>    (cond ((= state *foo*) 'foo)
>>>          ((= state *bar*) 'bar)
>>>          ((= state *quux*) 'quux)
>>>          (t 'unknown)))
>>
>>
>>
>> Try (case state (#.*foo* 'foo)....
>>
>> kenny
>>
> 
> 
> Is there a way to do this with strings ? (ie specifying the equality 
> test to perform) ?

Well, the language lawyers have already spanked my suggestion, so I 
think we better just code up macros which look like case but which 
expand into conds. There is nothing wrong with that, and indeed is a 
classic application for macros (I mention this only because I recall the 
OP expressing some reluctance to go the macro route).

kenny


-- 
Home? http://tilton-technology.com
Cells? http://www.common-lisp.net/project/cells/
Cello? http://www.common-lisp.net/project/cello/
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
Your Project Here! http://alu.cliki.net/Industry%20Application
From: rif
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <wj0d650tvym.fsf@five-percent-nation.mit.edu>
> >> Try (case state (#.*foo* 'foo)....
> >>
> >> kenny
> >>
> > Is there a way to do this with strings ? (ie specifying the equality
> > test to perform) ?
> 
> Well, the language lawyers have already spanked my suggestion, so I
> think we better just code up macros which look like case but which
> expand into conds. There is nothing wrong with that, and indeed is a
> classic application for macros (I mention this only because I recall
> the OP expressing some reluctance to go the macro route).
> 
> kenny
> 

Kenny,

I am currently using your suggestion, even though I understand it is
not guaranteed to work portably (it works fine on CMUCL).  If I care
more or come back to it, I will code up that macro.  I'm not opposed
to writing macros, I just feel like a bunch of other times, I've
written some moderately complex macro to do something, and six months
later I see on c.l.l. that what I needed to do is already built-in to
CL, often in a way that's better than what I provided.  So now, when I
think to myself "this ought to be built-in, but I can't seem to find
it", I ask.

Cheers,

rif
From: Hannah Schroeter
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <c8fpvr$sgi$1@c3po.use.schlund.de>
Hello!

Martin Raspaud  <········@labri.fr> wrote:
>[...]

>Is there a way to do this with strings ? (ie specifying the equality 
>test to perform) ?

Probably you'd have to write your own macro.

Like

(defmacro extended-case ((&key (test #'eql) evaluate-alternatives)
			 selector &rest clauses)
  (let ((sel (gensym "SELECTOR-"))
	(tst (gensym "TEST-")))
    `(let ((,sel ,selector)
	   (,tst ,test))
       (cond
	 ,@(mapcar #'(lambda (clause)
		       (if (or (eql (first clause) t)
			       (eql (first clause) :otherwise))
			 `(t ,@(rest clause))
			 `((funcall ,tst
				    ,sel
				    ,(if evaluate-alternatives
				       (first clause)
				       `',(first clause)))
			   ,@(rest clause))))
		   clauses)))))

Extending it so clauses can have *lists* of alternatives, like in case,
is left as an exercise to the reader :-)

Kind regards,

Hannah.
From: Ng Pheng Siong
Subject: Re: Need some combination of case and cond
Date: 
Message-ID: <c8darl$t2r$1@reader01.singnet.com.sg>
According to rif  <···@mit.edu>:
> Is there a cleaner/better way to say this in CL?
> 
> (defconstant *foo* 1)
> (defconstant *bar* 2)
> (defconstant *quux* 3)
> 
> (defun dispatch (state)
>    (cond ((= state *foo*) 'foo)
>          ((= state *bar*) 'bar)
>          ((= state *quux*) 'quux)
>          (t 'unknown)))

How about this?

(defgeneric dispatch (state))

(defmethod dispatch ((state (eql *foo*)))
    'foo)

(defmethod dispatch ((state (eql *bar*)))
    'bar)

(defmethod dispatch ((state (eql *quux*)))
    'quux)

(defmethod dispatch (state)
    'unknown)


-- 
Ng Pheng Siong <····@netmemetic.com> 

http://firewall.rulemaker.net -+- Firewall Change Management & Version Control
http://sandbox.rulemaker.net/ngps -+- ZServerSSL/Zope Windows Installers