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
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
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/
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
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/
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
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/
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
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.
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
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/
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
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.
> 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.
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.
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.
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.
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.
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.
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
+ 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
+ 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
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
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.
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
····@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)
+ 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
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.
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
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
> >> 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
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.