From: Greg Menke
Subject: "current working directory" within a package
Date: 
Message-ID: <m3vgn59pbb.fsf@europa.mindspring.com>
I'm finally succeeding with putting things in a package, exporting
them and using other packages.  Though I've no significant experience
with them yet, I think packages are wonderful.

I was wondering how to set up a per-package *default-path-defaults*
which is used automatically by the various forms in the normal way.

I <think> I've managed to get a "package-global"
*default-path-defaults* set, I exported it and the value stays even
when cl:*default-path-defaults* changes.  But, it looks as if things
like load & merge-pathnames are using cl:*default-path-defaults*, not
the shadow within my package.  My idea was to try and arrange a
"current working directory" within the package, based on
*default-path-default* so file operations would not need an explicit
path included.

I'm assuming *default-path-defaults* comes from cl, thats how I'm
interpreting describe's output.

I recall a discussion a short while ago regarding load-time vs
eval-time package issues, and was wondering if someone could expand on
the topic possibly with respect to the above.

Thanks,

Greg Menke

From: Kent M Pitman
Subject: Re: "current working directory" within a package
Date: 
Message-ID: <sfwwv7lm9rl.fsf@world.std.com>
Newsgroups: comp.lang.lisp
Subject: Re: "current working directory" within a package
References: <··············@europa.mindspring.com>
--text follows this line--
Greg Menke <··········@mindspring.com> writes:

> I'm finally succeeding with putting things in a package, exporting
> them and using other packages.  Though I've no significant experience
> with them yet, I think packages are wonderful.
> 
> I was wondering how to set up a per-package *default-path-defaults*
> which is used automatically by the various forms in the normal way.

I'm glad you're having a good time with packages, but this is going to
be a big lesson to you in how they are intended to be used.

First, the NAME of a symbol is used ONLY for getting you the symbol object
at INTERN time.  (Well, ok, you can write programs that probe at the name,
but that's an unusual situation, and certainly is not used when treating
symbols as variables.)  Once the symbol is interned, it's pointer identity
that counts, not name.

I don't know what this *default-path-defaults* thing is that you're 
talking about, but I'll assume you mean *default-pathname-defaults* used
by the system and not some other thing you've invented. In any case, the
point is that the system uses that symbol by pointer, not by name, in load,
so it would never have any reason to look at similarly  named symbols in
other packages any more than it would have reason to look in any other
symbol.  The symbol it uses for value is the right symbol, and all other
symbols are the wrong symbol; there is no middle ground.

A package is not a container that you put things in, like a class.  It
is instead just a special-purpose and somewhat elaborately constructed
hash-table-like structure, mapping symbol names to symbol objects.

> I <think> I've managed to get a "package-global"
> *default-path-defaults* set, I exported it and the value stays even
> when cl:*default-path-defaults* changes.

This means you have a separated symbol.  It doesn't at that point matter if
the symbol is *foo* or fred or some-other-package::fred.  At this point, all
that matters is that it is not the symbol that LOAD will be looking in.

LOAD doesn't take the time to do a name lookup.  LOAD's code contains an
actual pointer to the value cell location of the actual symbol 
*default-pathname-defaults*.  It just accesses the value of that cell
and is done.  If you were to rename that symbol by forceable mutation,
you would probably break your Lisp's ability to intern and print that
symbol, but LOAD would still merrily access the symbol's value cell 
unperturbed.

> But, it looks as if things
> like load & merge-pathnames are using cl:*default-path-defaults*, not
> the shadow within my package.

Exactly.

> My idea was to try and arrange a
> "current working directory" within the package, based on
> *default-path-default* so file operations would not need an explicit
> path included.

I do *not* recommend you doing this, but to help you understand the 
mechanisms involved, here's what would need to happen to make things work
as you expect:

   You'd have to shadow LOAD and MERGE-PATHNAMES and any other symbols
   you wanted to define to do this different lookup, and you'd have to
   make them do a more complicated lookup.

   (defun greg-utils:load (filespec &rest load-keys)
     (let ((cl:*default-pathname-defaults*
             (let ((symbol (intern "*DEFAULT-PATHNAME-DEFAULTS*"
				   *package*)))
               (if (boundp symbol)
                   (symbol-value symbol)
                 cl:*default-pathname-defaults*))))
       (apply #'cl:load filespec load-keys)))

   Normally, a special variable lookup in compiled Lisp code is a memory
   move from a known location--i.e., very fast.  (Process switches with
   bound special variables are sometimes slowed in order to make this 
   operation fast.)  Instead, you're going to do an INTERN (differs between
   implementations, but I'd bet there are one or two hundred instructions
   typically done in there), and then a boundp check (to make sure you didn't
   find a non-cooperating package that needs to be "helped").  The boundp
   check is small cost compared to the INTERN, which is hugely expensive.

   Package operations like this are just not intended to be done at runtime.

> I'm assuming *default-path-defaults* comes from cl, thats how I'm
> interpreting describe's output.

*default-pathname-defaults*.

You should definitely get a copy of CLHS (the Common Lisp HyperSpec)
to use as a reference. You can download it from Xanalys.

> I recall a discussion a short while ago regarding load-time vs
> eval-time package issues, and was wondering if someone could expand on
> the topic possibly with respect to the above.

Style tip:  don't do eval-time package stuff except to in boostrap code
 for your system, or in implementing an embedded language.  normal programs
 have no business calling INTERN.  

Also: INTERN has a SEVERE cost in that it makes it opaque to the gc
 which symbols are "in use".  New symbols could come into use through INTERN.
 So useful "tree-shaking" (getting rid of unused junk by examining what 
 symbols are accessible) is impossible in an application that calls INTERN at 
 runtime.
From: Greg Menke
Subject: Re: "current working directory" within a package
Date: 
Message-ID: <m3r8xsaja2.fsf@europa.mindspring.com>
> I don't know what this *default-path-defaults* thing is that you're 
> talking about, but I'll assume you mean *default-pathname-defaults* used

Thats what I meant.  I found the first 3 typos in every reference
before I posted, naturally missing the last one...


> > My idea was to try and arrange a
> > "current working directory" within the package, based on
> > *default-path-default* so file operations would not need an explicit
> > path included.
> 
> I do *not* recommend you doing this, but to help you understand the 
> mechanisms involved, here's what would need to happen to make things work
> as you expect:

Well, I certainly didn't want to implement something dreadful- only
what I thought would be useful.  From your discussion, I figure a
better approach would save *default-pathname-defaults* someplace, then
use the saved value as appropriate in file operations within the
package.


> You should definitely get a copy of CLHS (the Common Lisp HyperSpec)
> to use as a reference. You can download it from Xanalys.

I have it.  Both as a local copy via file url in Netscape and exported
via cl-http.  I've found it extremely useful, though no doubt I
frequently miss the forest for the trees.

Thanks,

Greg Menke
From: Kent M Pitman
Subject: Re: "current working directory" within a package
Date: 
Message-ID: <sfwzocglqt5.fsf@world.std.com>
Greg Menke <··········@mindspring.com> writes:

> > I don't know what this *default-path-defaults* thing is that you're 
> > talking about, but I'll assume you mean *default-pathname-defaults* used
> 
> Thats what I meant.  I found the first 3 typos in every reference
> before I posted, naturally missing the last one...
> 
> 
> > > My idea was to try and arrange a
> > > "current working directory" within the package, based on
> > > *default-path-default* so file operations would not need an explicit
> > > path included.
> > 
> > I do *not* recommend you doing this, but to help you understand the 
> > mechanisms involved, here's what would need to happen to make things work
> > as you expect:
> 
> Well, I certainly didn't want to implement something dreadful- only
> what I thought would be useful.  From your discussion, I figure a
> better approach would save *default-pathname-defaults* someplace, then
> use the saved value as appropriate in file operations within the
> package.

Maybe.  Though I'd like to see an example.

In practice, there are probably several common patterns of usage.
e.g.,

 * You might write a system which has an external data directory.
   e.g., a mailer might like a queue directory or an http server
   might have a default place to find files or a user profile system
   might have a directory full of per-user defaults.  These are most
   commonly handled by having the individual system have a documented
   variable appropriate to the task.  e.g.,
     my-mailer:*mail-queue-directory*
   or
     my-http:*site-root-pathname*
   or
     my-profiles:*user-profile-directory*

 * You might write a system that opens files for a user. e.g., something
   like (read-xml-file ...).  Here there is no reason to suppose that
   people find xml files in a different directory than they find other
   files, so you should just default through *default-pathname-defaults*
   because the user may already have changed this value in an init file
   and you'll be fighting their stated desire if you don't.

 * You might write a system that offers libraries defined by users and 
   others.  Here you might make a search list. e.g.,
     my-editor:*library-directories*

 * You might have a system which has its own private files to load out of
   a fixed set.  In this case, you shouldn't change the defaults but should
   instead use fully qualified pathnames.  You can do this in several ways.
 
    * Load by merging relative pathnames against *load-truename* or
      *compile-file-truename*  [This is what I recommend.] e.g.,
        (load (merge-pathnames "foo2.lisp" *load-truename*))
      This gets a fully qualified pathname by fillingin defaults from the
      file presently being loaded.
    * Define a logical host, and load the file from the logical host.
    * Load from a fixed filename, insisting that the user must use that dir.
    * Load from a user-settable root filename, giving the user the ability     
      to set it.

If you have some other situation than one of these that you think really
calls for per-package defaults, I'd like to hear it explained.

> > You should definitely get a copy of CLHS (the Common Lisp HyperSpec)
> > to use as a reference. You can download it from Xanalys.
> 
> I have it.  Both as a local copy via file url in Netscape and exported
> via cl-http.  I've found it extremely useful, though no doubt I
> frequently miss the forest for the trees.

Hey! No trees were harmed in the production of CLHS!
(That's one of its big advantages.)
From: Greg Menke
Subject: Re: "current working directory" within a package
Date: 
Message-ID: <m3n18gagfl.fsf@europa.mindspring.com>
> > Well, I certainly didn't want to implement something dreadful- only
> > what I thought would be useful.  From your discussion, I figure a
> > better approach would save *default-pathname-defaults* someplace, then
> > use the saved value as appropriate in file operations within the
> > package.
> 
> In practice, there are probably several common patterns of usage.
> e.g.,
> 
>  * You might write a system which has an external data directory.
>    e.g., a mailer might like a queue directory or an http server
>    might have a default place to find files or a user profile system
>    might have a directory full of per-user defaults.  These are most
>    commonly handled by having the individual system have a documented
>    variable appropriate to the task.  e.g.,

This is the case, my package encapsulates a couple cl-http exports,
several static, another computed which emits the lisp file from which
its loaded;


(defpackage "GWEB" 
	(:shadow "*KEYWORD-PACKAGE*" "*DEFAULT-PATHNAME-DEFAULTS*")
	(:export "L" "*DEFAULT-PATHNAME*")
	(:use common-lisp common-lisp-user lispworks url http) )

(in-package "GWEB")

(defvar *default-pathname* (pathname *default-pathname-defaults*))

(defmethod test-http ((testurl http-url) stream)
  (with-conditional-get-response (stream :html :expires (expiration-universal-time testurl)
					  :cache-control (response-cache-control-directives testurl)
					  :content-language (languages testurl))
    (html3.2:with-html-document (:declare-dtd-version-p t :stream stream)
       (html3.2:with-standard-document-body (:stream stream)
	 (image-line :stream stream)
	<snip>

(export-url #u"/cl-http/icons/hqn.gif" <snip>
(export-url #u"/test.html" <snip>
(export-url #u"/docs/cltl2/" <snip>
(export-url #u"/docs/hyperspec/" <snip>

(defun l () (load (merge-pathnames *default-pathname* "test.lisp") :print t))


The above is highly snipped, but just after in-package, a "package
global" symbol is set to *default-pathname-defaults* at load time.
This symbol is used in the computed url (details not shown for
brevity) and within load inside the "l" defun at the bottom to reload
the source when I change it.  No doubt I'm committing various sins,
please be gentle... ;)


> > I have it.  Both as a local copy via file url in Netscape and exported
> > via cl-http.  I've found it extremely useful, though no doubt I
> > frequently miss the forest for the trees.
> 
> Hey! No trees were harmed in the production of CLHS!
> (That's one of its big advantages.)

:)  In my case its probably more like throwing pearls before swine...


Greg Menke