From: Joshua Taylor
Subject: read-time-eval as case/ecase keyforms
Date: 
Message-ID: <25a34e0e-9210-4341-adab-6160fa638cd5@27g2000hsf.googlegroups.com>
Hi all, I'm hoping someone can tell me what I'm doing wrong here. I'm
trying to define some constants ahead of time so that I can write case
and ecase statements with #. . I've got code in three files:

;;;; ksymbols.lisp:

(defstruct ksymbol name)

(defvar *ksymbols* (make-hash-table :test 'equal))

(defun intern-ksymbol (name)
  (multiple-value-bind (ksymbol present?) (gethash name *ksymbols*)
    (if present? ksymbol
      (setf (gethash name *ksymbols*)
            (make-ksymbol :name name)))))

;;;; constants.lisp

(defconstant knil
  (intern-ksymbol "NIL"))

(defconstant kt
  (intern-ksymbol "T"))

;;;; code.lisp

(defun problem ()
  (ecase (intern-ksymbol "NIL")
    (#.kt  kt)
    (#.knil knil)))


Now if I load the files in order, here's the behavior I get the
behavior I'd expect (i.e., I get the knil object back):

CL-USER 1 > (load "~/Desktop/problem/ksymbols.lisp")
; Loading text file /Users/tayloj/Desktop/problem/ksymbols.lisp
#P"/Users/tayloj/Desktop/problem/ksymbols.lisp"

CL-USER 2 > (load "~/Desktop/problem/constants.lisp")
; Loading text file /Users/tayloj/Desktop/problem/constants.lisp
#P"/Users/tayloj/Desktop/problem/constants.lisp"

CL-USER 3 > (load "~/Desktop/problem/code.lisp")
; Loading text file /Users/tayloj/Desktop/problem/code.lisp
#P"/Users/tayloj/Desktop/problem/code.lisp"

CL-USER 4 > (problem)
#S(KSYMBOL :NAME "NIL")

But if I compile the files (and load via :load t) I get this behavior:

CL-USER 5 > (compile-file "~/Desktop/problem/ksymbols.lisp" :load t)
;;; Compiling file ~/Desktop/problem/ksymbols.lisp ...
;;; Safety = 3, Speed = 1, Space = 1, Float = 1, Interruptible = 0
;;; Compilation speed = 1, Debug = 2, Fixnum safety = 3
;;; Source level debugging is on
;;; Source file recording is  on
;;; Cross referencing is on
; (TOP-LEVEL-FORM 0)
; (SUBFUNCTION (DEFSETF KSYMBOL-NAME) (DEFSTRUCT KSYMBOL))
; (SUBFUNCTION MAKE-KSYMBOL (DEFSTRUCT KSYMBOL))
; (SUBFUNCTION MAKE-KSYMBOL (DEFSTRUCT KSYMBOL))
; (DEFVAR *KSYMBOLS*)
; INTERN-KSYMBOL
; (TOP-LEVEL-FORM 0)
; Loading fasl file /Users/tayloj/Desktop/problem/ksymbols.nfasl
#P"/Users/tayloj/Desktop/problem/ksymbols.nfasl"
NIL
NIL

CL-USER 6 > (compile-file "~/Desktop/problem/constants.lisp" :load t)
;;; Compiling file ~/Desktop/problem/constants.lisp ...
;;; Safety = 3, Speed = 1, Space = 1, Float = 1, Interruptible = 0
;;; Compilation speed = 1, Debug = 2, Fixnum safety = 3
;;; Source level debugging is on
;;; Source file recording is  on
;;; Cross referencing is on
; (TOP-LEVEL-FORM 0)
; (DEFCONSTANT KNIL)
; (DEFCONSTANT KT)
; (TOP-LEVEL-FORM 0)
; Loading fasl file /Users/tayloj/Desktop/problem/constants.nfasl
#P"/Users/tayloj/Desktop/problem/constants.nfasl"
NIL
NIL

CL-USER 7 > (compile-file "~/Desktop/problem/code.lisp" :load t)
;;; Compiling file ~/Desktop/problem/code.lisp ...
;;; Safety = 3, Speed = 1, Space = 1, Float = 1, Interruptible = 0
;;; Compilation speed = 1, Debug = 2, Fixnum safety = 3
;;; Source level debugging is on
;;; Source file recording is  on
;;; Cross referencing is on
; (TOP-LEVEL-FORM 0)
; PROBLEM
; (TOP-LEVEL-FORM 0)
; Loading fasl file /Users/tayloj/Desktop/problem/code.nfasl
#P"/Users/tayloj/Desktop/problem/code.nfasl"
NIL
NIL

CL-USER 8 > (problem)

Error: #S(KSYMBOL :NAME "NIL") fell through ECASE expression.
Wanted one of (#S(KSYMBOL :NAME "T") #S(KSYMBOL :NAME "NIL")).
  1 (abort) Return to level 0.
  2 Return to top loop level 0.

Type :b for backtrace, :c <option number> to proceed,  or :? for other
options

CL-USER 9 : 1 >

This may be the proper behavior, but I don't understand why, and I'm
hoping someone can point me to the right section in the hyperspec, or
could explain what's happening. Thanks!

From: Joshua Taylor
Subject: Re: read-time-eval as case/ecase keyforms
Date: 
Message-ID: <5b811e1c-eb2a-4278-bcab-2df863de16aa@e39g2000hsf.googlegroups.com>
On Jun 4, 2:37 pm, Joshua Taylor <···········@gmail.com> wrote:
> Hi all, I'm hoping someone can tell me what I'm doing wrong here. I'm
> trying to define some constants ahead of time so that I can write case
> and ecase statements with #. . I've got code in three files:
>
> ;;;; ksymbols.lisp:
>
> (defstruct ksymbol name)
>
> (defvar *ksymbols* (make-hash-table :test 'equal))
>
> (defun intern-ksymbol (name)
>   (multiple-value-bind (ksymbol present?) (gethash name *ksymbols*)
>     (if present? ksymbol
>       (setf (gethash name *ksymbols*)
>             (make-ksymbol :name name)))))
>
> ;;;; constants.lisp
>
> (defconstant knil
>   (intern-ksymbol "NIL"))
>
> (defconstant kt
>   (intern-ksymbol "T"))
>
> ;;;; code.lisp
>
> (defun problem ()
>   (ecase (intern-ksymbol "NIL")
>     (#.kt  kt)
>     (#.knil knil)))
>
> Now if I load the files in order, here's the behavior I get the
> behavior I'd expect (i.e., I get the knil object back):
>
> CL-USER 1 > (load "~/Desktop/problem/ksymbols.lisp")
> ; Loading text file /Users/tayloj/Desktop/problem/ksymbols.lisp
> #P"/Users/tayloj/Desktop/problem/ksymbols.lisp"
>
> CL-USER 2 > (load "~/Desktop/problem/constants.lisp")
> ; Loading text file /Users/tayloj/Desktop/problem/constants.lisp
> #P"/Users/tayloj/Desktop/problem/constants.lisp"
>
> CL-USER 3 > (load "~/Desktop/problem/code.lisp")
> ; Loading text file /Users/tayloj/Desktop/problem/code.lisp
> #P"/Users/tayloj/Desktop/problem/code.lisp"
>
> CL-USER 4 > (problem)
> #S(KSYMBOL :NAME "NIL")
>
> But if I compile the files (and load via :load t) I get this behavior:
>
> CL-USER 5 > (compile-file "~/Desktop/problem/ksymbols.lisp" :load t)
> ;;; Compiling file ~/Desktop/problem/ksymbols.lisp ...
> ;;; Safety = 3, Speed = 1, Space = 1, Float = 1, Interruptible = 0
> ;;; Compilation speed = 1, Debug = 2, Fixnum safety = 3
> ;;; Source level debugging is on
> ;;; Source file recording is  on
> ;;; Cross referencing is on
> ; (TOP-LEVEL-FORM 0)
> ; (SUBFUNCTION (DEFSETF KSYMBOL-NAME) (DEFSTRUCT KSYMBOL))
> ; (SUBFUNCTION MAKE-KSYMBOL (DEFSTRUCT KSYMBOL))
> ; (SUBFUNCTION MAKE-KSYMBOL (DEFSTRUCT KSYMBOL))
> ; (DEFVAR *KSYMBOLS*)
> ; INTERN-KSYMBOL
> ; (TOP-LEVEL-FORM 0)
> ; Loading fasl file /Users/tayloj/Desktop/problem/ksymbols.nfasl
> #P"/Users/tayloj/Desktop/problem/ksymbols.nfasl"
> NIL
> NIL
>
> CL-USER 6 > (compile-file "~/Desktop/problem/constants.lisp" :load t)
> ;;; Compiling file ~/Desktop/problem/constants.lisp ...
> ;;; Safety = 3, Speed = 1, Space = 1, Float = 1, Interruptible = 0
> ;;; Compilation speed = 1, Debug = 2, Fixnum safety = 3
> ;;; Source level debugging is on
> ;;; Source file recording is  on
> ;;; Cross referencing is on
> ; (TOP-LEVEL-FORM 0)
> ; (DEFCONSTANT KNIL)
> ; (DEFCONSTANT KT)
> ; (TOP-LEVEL-FORM 0)
> ; Loading fasl file /Users/tayloj/Desktop/problem/constants.nfasl
> #P"/Users/tayloj/Desktop/problem/constants.nfasl"
> NIL
> NIL
>
> CL-USER 7 > (compile-file "~/Desktop/problem/code.lisp" :load t)
> ;;; Compiling file ~/Desktop/problem/code.lisp ...
> ;;; Safety = 3, Speed = 1, Space = 1, Float = 1, Interruptible = 0
> ;;; Compilation speed = 1, Debug = 2, Fixnum safety = 3
> ;;; Source level debugging is on
> ;;; Source file recording is  on
> ;;; Cross referencing is on
> ; (TOP-LEVEL-FORM 0)
> ; PROBLEM
> ; (TOP-LEVEL-FORM 0)
> ; Loading fasl file /Users/tayloj/Desktop/problem/code.nfasl
> #P"/Users/tayloj/Desktop/problem/code.nfasl"
> NIL
> NIL
>
> CL-USER 8 > (problem)
>
> Error: #S(KSYMBOL :NAME "NIL") fell through ECASE expression.
> Wanted one of (#S(KSYMBOL :NAME "T") #S(KSYMBOL :NAME "NIL")).
>   1 (abort) Return to level 0.
>   2 Return to top loop level 0.
>
> Type :b for backtrace, :c <option number> to proceed,  or :? for other
> options
>
> CL-USER 9 : 1 >
>
> This may be the proper behavior, but I don't understand why, and I'm
> hoping someone can point me to the right section in the hyperspec, or
> could explain what's happening. Thanks!

I should have mentioned version information: I'm running Lispworks 5.1
on Mac OS X 10.5.
From: Joshua Taylor
Subject: Re: read-time-eval as case/ecase keyforms
Date: 
Message-ID: <a3b5de52-3014-4342-a4a1-d0acd568067c@y38g2000hsy.googlegroups.com>
On Jun 4, 2:48 pm, Joshua Taylor <···········@gmail.com> wrote:
> On Jun 4, 2:37 pm, Joshua Taylor <···········@gmail.com> wrote:
>
>
>
> > Hi all, I'm hoping someone can tell me what I'm doing wrong here. I'm
> > trying to define some constants ahead of time so that I can write case
> > and ecase statements with #. . I've got code in three files:
>
> > ;;;; ksymbols.lisp:
>
> > (defstruct ksymbol name)
>
> > (defvar *ksymbols* (make-hash-table :test 'equal))
>
> > (defun intern-ksymbol (name)
> >   (multiple-value-bind (ksymbol present?) (gethash name *ksymbols*)
> >     (if present? ksymbol
> >       (setf (gethash name *ksymbols*)
> >             (make-ksymbol :name name)))))
>
> > ;;;; constants.lisp
>
> > (defconstant knil
> >   (intern-ksymbol "NIL"))
>
> > (defconstant kt
> >   (intern-ksymbol "T"))
>
> > ;;;; code.lisp
>
> > (defun problem ()
> >   (ecase (intern-ksymbol "NIL")
> >     (#.kt  kt)
> >     (#.knil knil)))
>
> > Now if I load the files in order, here's the behavior I get the
> > behavior I'd expect (i.e., I get the knil object back):
>
> > CL-USER 1 > (load "~/Desktop/problem/ksymbols.lisp")
> > ; Loading text file /Users/tayloj/Desktop/problem/ksymbols.lisp
> > #P"/Users/tayloj/Desktop/problem/ksymbols.lisp"
>
> > CL-USER 2 > (load "~/Desktop/problem/constants.lisp")
> > ; Loading text file /Users/tayloj/Desktop/problem/constants.lisp
> > #P"/Users/tayloj/Desktop/problem/constants.lisp"
>
> > CL-USER 3 > (load "~/Desktop/problem/code.lisp")
> > ; Loading text file /Users/tayloj/Desktop/problem/code.lisp
> > #P"/Users/tayloj/Desktop/problem/code.lisp"
>
> > CL-USER 4 > (problem)
> > #S(KSYMBOL :NAME "NIL")
>
> > But if I compile the files (and load via :load t) I get this behavior:
>
> > CL-USER 5 > (compile-file "~/Desktop/problem/ksymbols.lisp" :load t)
> > ;;; Compiling file ~/Desktop/problem/ksymbols.lisp ...
> > ;;; Safety = 3, Speed = 1, Space = 1, Float = 1, Interruptible = 0
> > ;;; Compilation speed = 1, Debug = 2, Fixnum safety = 3
> > ;;; Source level debugging is on
> > ;;; Source file recording is  on
> > ;;; Cross referencing is on
> > ; (TOP-LEVEL-FORM 0)
> > ; (SUBFUNCTION (DEFSETF KSYMBOL-NAME) (DEFSTRUCT KSYMBOL))
> > ; (SUBFUNCTION MAKE-KSYMBOL (DEFSTRUCT KSYMBOL))
> > ; (SUBFUNCTION MAKE-KSYMBOL (DEFSTRUCT KSYMBOL))
> > ; (DEFVAR *KSYMBOLS*)
> > ; INTERN-KSYMBOL
> > ; (TOP-LEVEL-FORM 0)
> > ; Loading fasl file /Users/tayloj/Desktop/problem/ksymbols.nfasl
> > #P"/Users/tayloj/Desktop/problem/ksymbols.nfasl"
> > NIL
> > NIL
>
> > CL-USER 6 > (compile-file "~/Desktop/problem/constants.lisp" :load t)
> > ;;; Compiling file ~/Desktop/problem/constants.lisp ...
> > ;;; Safety = 3, Speed = 1, Space = 1, Float = 1, Interruptible = 0
> > ;;; Compilation speed = 1, Debug = 2, Fixnum safety = 3
> > ;;; Source level debugging is on
> > ;;; Source file recording is  on
> > ;;; Cross referencing is on
> > ; (TOP-LEVEL-FORM 0)
> > ; (DEFCONSTANT KNIL)
> > ; (DEFCONSTANT KT)
> > ; (TOP-LEVEL-FORM 0)
> > ; Loading fasl file /Users/tayloj/Desktop/problem/constants.nfasl
> > #P"/Users/tayloj/Desktop/problem/constants.nfasl"
> > NIL
> > NIL
>
> > CL-USER 7 > (compile-file "~/Desktop/problem/code.lisp" :load t)
> > ;;; Compiling file ~/Desktop/problem/code.lisp ...
> > ;;; Safety = 3, Speed = 1, Space = 1, Float = 1, Interruptible = 0
> > ;;; Compilation speed = 1, Debug = 2, Fixnum safety = 3
> > ;;; Source level debugging is on
> > ;;; Source file recording is  on
> > ;;; Cross referencing is on
> > ; (TOP-LEVEL-FORM 0)
> > ; PROBLEM
> > ; (TOP-LEVEL-FORM 0)
> > ; Loading fasl file /Users/tayloj/Desktop/problem/code.nfasl
> > #P"/Users/tayloj/Desktop/problem/code.nfasl"
> > NIL
> > NIL
>
> > CL-USER 8 > (problem)
>
> > Error: #S(KSYMBOL :NAME "NIL") fell through ECASE expression.
> > Wanted one of (#S(KSYMBOL :NAME "T") #S(KSYMBOL :NAME "NIL")).
> >   1 (abort) Return to level 0.
> >   2 Return to top loop level 0.
>
> > Type :b for backtrace, :c <option number> to proceed,  or :? for other
> > options
>
> > CL-USER 9 : 1 >
>
> > This may be the proper behavior, but I don't understand why, and I'm
> > hoping someone can point me to the right section in the hyperspec, or
> > could explain what's happening. Thanks!
>
> I should have mentioned version information: I'm running Lispworks 5.1
> on Mac OS X 10.5.

Sorry to reply so many times to mine own message, but I should also
mention that the error that comes about in running problem also
happens when I do all the compile-file(+ :load t) in a fresh image,
not just after load-file-ing.
From: John Thingstad
Subject: Re: read-time-eval as case/ecase keyforms
Date: 
Message-ID: <op.ub8oxmitut4oq5@pandora.alfanett.no>
P� Wed, 04 Jun 2008 20:53:28 +0200, skrev Joshua Taylor  
<···········@gmail.com>:


>>
>> > This may be the proper behavior, but I don't understand why, and I'm
>> > hoping someone can point me to the right section in the hyperspec, or
>> > could explain what's happening. Thanks!
>>
>> I should have mentioned version information: I'm running Lispworks 5.1
>> on Mac OS X 10.5.
>
> Sorry to reply so many times to mine own message, but I should also
> mention that the error that comes about in running problem also
> happens when I do all the compile-file(+ :load t) in a fresh image,
> not just after load-file-ing.

Seems to me you have the problem because the variables are not available  
at readtime.

ecase requires a value for dispath. So you nead a (eval-when  
:compile-toplevel :load :execute)
around defconstant's you want to use.
Also a #. (read-eval) in front of the constant.

--------------
John Thingstad
From: Joshua Taylor
Subject: Re: read-time-eval as case/ecase keyforms
Date: 
Message-ID: <8fff6d1e-eaf7-4580-9650-cf447aed5616@26g2000hsk.googlegroups.com>
On Jun 4, 3:40 pm, "John Thingstad" <·······@online.no> wrote:
> På Wed, 04 Jun 2008 20:53:28 +0200, skrev Joshua Taylor  
> <···········@gmail.com>:
>
>
>
> >> > This may be the proper behavior, but I don't understand why, and I'm
> >> > hoping someone can point me to the right section in the hyperspec, or
> >> > could explain what's happening. Thanks!
>
> >> I should have mentioned version information: I'm running Lispworks 5.1
> >> on Mac OS X 10.5.
>
> > Sorry to reply so many times to mine own message, but I should also
> > mention that the error that comes about in running problem also
> > happens when I do all the compile-file(+ :load t) in a fresh image,
> > not just after load-file-ing.
>
> Seems to me you have the problem because the variables are not available  
> at readtime.
>
> ecase requires a value for dispath. So you nead a (eval-when  
> :compile-toplevel :load :execute)
> around defconstant's you want to use.
> Also a #. (read-eval) in front of the constant.
>
> --------------
> John Thingstad

Updating constants.lisp to be:

(eval-when (:compile-toplevel :load-toplevel :execute)
(defconstant knil
  (intern-ksymbol "NIL")))

(eval-when (:compile-toplevel :load-toplevel :execute)
(defconstant kt
  (intern-ksymbol "T")))

I still don't get the desired behavior:


CL-USER 1 > (compile-file "ksymbols.lisp" :load t)
;;; Compiling file ksymbols.lisp ...

CL-USER 2 > (compile-file "constants.lisp" :load t)
;;; Compiling file constants.lisp ...

CL-USER 3 > (compile-file "code.lisp" :load t)
;;; Compiling file code.lisp ...

CL-USER 4 > (problem)

Error: #S(KSYMBOL :NAME "NIL") fell through ECASE expression.
Wanted one of (#S(KSYMBOL :NAME "T") #S(KSYMBOL :NAME "NIL")).
  1 (abort) Return to level 0.
  2 Return to top loop level 0.

Type :b for backtrace, :c <option number> to proceed,  or :? for other
options

CL-USER 5 : 1 >

The ecase in problem does have #. in front of the particular constants
I'm trying to reference (it did before as well; I'm just clarifying).
Since the file containing the constants was compiled and loaded before
the file containing the definition of problem, It would seem that the
kt and knil ought to be available. If the constants aren't defined by
then, I'm not sure when else they could be.

//JT
From: Willem Broekema
Subject: Re: read-time-eval as case/ecase keyforms
Date: 
Message-ID: <24b5979c-512d-42e9-9868-678a4835049b@b1g2000hsg.googlegroups.com>
On Jun 4, 8:37 pm, Joshua Taylor <···········@gmail.com> wrote:
> Error: #S(KSYMBOL :NAME "NIL") fell through ECASE expression.
> Wanted one of (#S(KSYMBOL :NAME "T") #S(KSYMBOL :NAME "NIL")).
> . . .
> This may be the proper behavior, but I don't understand why, and I'm
> hoping someone can point me to the right section in the hyperspec, or
> could explain what's happening. Thanks!

When the function problem gets compiled, the compiled code contains
the two ksymbol structs as literal objects. When the file is loaded,
the two literal objects will be revived. Your problem is, that the
revived objects you get back are not eq to the objects that exist
already (contained in *ksymbols*). The default behaviour of structs in
compiled code is explicitly undefined even, see 3.2.4.2.2 "Definition
of Similarity".

The solution consists of making sure the revived objects are eq, or
more precice, not storing the literal object itself in the compiled
code, but storing a creation form that refers to function intern-
ksymbol in order to prevent duplicates:

(defmethod make-load-form ((x ksymbol) &optional env)
  (declare (ignore env))
  `(intern-ksymbol ',(ksymbol-name x)))

Note that if you used interned symbols directly, instead of structs
that wrap symbols, you would automatically benefit from the standard
behavior that loading literal interned symbols does not lead to
duplicates (see 3.2.4.2.2 again).


- Willem
From: Joshua Taylor
Subject: Re: read-time-eval as case/ecase keyforms
Date: 
Message-ID: <5f4c6a0c-fb8d-43bd-9f89-f615154143fb@f36g2000hsa.googlegroups.com>
On Jun 4, 3:59 pm, Willem Broekema <········@gmail.com> wrote:
> On Jun 4, 8:37 pm, Joshua Taylor <···········@gmail.com> wrote:
>
> > Error: #S(KSYMBOL :NAME "NIL") fell through ECASE expression.
> > Wanted one of (#S(KSYMBOL :NAME "T") #S(KSYMBOL :NAME "NIL")).
> > . . .
> > This may be the proper behavior, but I don't understand why, and I'm
> > hoping someone can point me to the right section in the hyperspec, or
> > could explain what's happening. Thanks!
>
> When the function problem gets compiled, the compiled code contains
> the two ksymbol structs as literal objects. When the file is loaded,
> the two literal objects will be revived. Your problem is, that the
> revived objects you get back are not eq to the objects that exist
> already (contained in *ksymbols*). The default behaviour of structs in
> compiled code is explicitly undefined even, see 3.2.4.2.2 "Definition
> of Similarity".
>
> The solution consists of making sure the revived objects are eq, or
> more precice, not storing the literal object itself in the compiled
> code, but storing a creation form that refers to function intern-
> ksymbol in order to prevent duplicates:
>
> (defmethod make-load-form ((x ksymbol) &optional env)
>   (declare (ignore env))
>   `(intern-ksymbol ',(ksymbol-name x)))
>
> Note that if you used interned symbols directly, instead of structs
> that wrap symbols, you would automatically benefit from the standard
> behavior that loading literal interned symbols does not lead to
> duplicates (see 3.2.4.2.2 again).
>
> - Willem

Ah, this explains the issue very well—Thank you!

The example I used here with 'ksymbols' is a simplification of the
issue I'm actually facing. I'm working with some RDF and I've defined
some constant PURI [http://puri.b9.com/] uris such as +rdf-about+ and
+rdf-node-id+ and was trying to write some case/ecase expressions
using them. For the moment I've gone back to (cond ((eq uri +rdf-about
+) ...) ((eq uri +rdf-node-id+) ...)). Since I'm not the maintainer of
the PURI code, it might be bad form to start writing make-load-form
methods, but maybe with some subclassing...

Anyhow, you've pinned down what the problem is, and what to
investigate as solutions. Thanks! I'm always pleased when it's pointed
out that there are subtle issues that I hadn't considered, and then
that the spec already addressed them.

//JT