Hi!
I'm working in a medium-sized project using Common Lisp (particularly CLisp).
The code is relatively low-level with heavy use of structs and functions
to manipulate them but almost no use of classes. There are about 15/20 files
and, except for a few of them, the dependencies between the other are strong
enough to justify giving every one full access to the "public" symbols of the
rest in a simple way (I mean, not listing a lot of exported symbols or,
similarly, a lot of imported ones). But, as there is also a lot of auxiliary
machinery (functions, macros, structs, variables, etc) that is logically
local to each module/file I wouldn't like to make everything visible from
outside in order to avoid name clashes. One of the solutions that came
to my mind was using anonymous packages (automatically generated by a macro)
which import everything in the main package (the package where every module
is and from which global definitions can be accesed) without exporting
anything. Something like a "namespace { .... }" in C++ that uses another
namespace. Although this is factible, another solution seems more elegant
(at least to me): using lexical scopes. So I write a simple macro similar
to this one:
(defmacro lex (&rest forms)
(let ((label-list '())
(let-list '())
(macrolet-list '())
(body '()))
(dolist (form forms)
(case (car form)
(ldefun (back-push (cdr form) label-list))
(ldefmacro (back-push (cdr form) macrolet-list))
(ldefvar (back-push (cdr form) let-list))
(otherwise (back-push form body))))
`(macrolet ,macrolet-list
(let* ,let-list
(labels ,label-list
,@body)))))
The use of this macro gives me some freedom to reorder the definitions at the
top level (I mean what is seen as being at the file top level, not the
top-level as defined by the Hyperspec) instead of putting every local
macro first, then every local var, then every local function and at the
end the global definitions. But this is only a matter of taste and also
has its limitations (the order is fixed, the symbols created by defstruct can�t
be made local, etc). Using this (and a few other) macros, a module would look
like:
(provide-module "module-1")
(require-module "module-2")
....
(require-module "module-n")
(in-package main-package)
(lex
(ldefvar local-var-1 ...)
(defun global-function-1 (...)
....)
(defmacro global-macro-1 (...)
....)
(ldefun local-function-1 (...)
....)
(defvar global-var-1 ...)
(ldefun local-function-2 (...)
....)
)
(The provide-module/require-module macros eval when
(:compile-toplevel :load-toplevel :execute), so they work fine with the
compiler. Their semantics are similar to those of provide/require.)
But the code above has a subtle problem when compiled: global-var-1 and
global-macro-1 are not top-level (I mean top-level as defined in the Hyperspec):
they are wrapped into (macrolet (let* (labels ....))).
According to the Hyperspec (3.2.3.1.1 Processing of Defining Macros):
Defining macros (such as defmacro or defvar) appearing within a file
being processed by compile-file normally have compile-time side
effects which affect how subsequent forms in the same file are
compiled. A convenient model for explaining how these side effects
happen is that the defining macro expands into one or more eval-when
forms, and that the calls which cause the compile-time side effects to
happen appear in the body of an (eval-when (:compile-toplevel) ...)
form.
The compile-time side effects may cause information about the
definition to be stored differently than if the defining macro had
been processed in the `normal' way (either interpretively or by
loading the compiled file).
....
As they aren't top-level the compiler will never execute the
(eval-when (:compile-toplevel) ...) part of (defmacro global-macro-1 ...)
and (defvar global-var-1 ...). This can't be fixed rewriting lexenv
to be evaluated when (:compile-toplevel) cause this would execute the
(eval-when (:execute) ...) part of (defmacro global-macro-1 ...)
and (defvar global-var-1 ...) (I'm only following the "covenient model"
and the rules in "3.2.3.1 Processing of Top Level Forms" here).
What do you think? Is there a solution for this compilation problem? Or
am I facing the problem the wrong way and should I try another approach (for
example, the anonymous package one)?
Thank you very much,
Carlos
"Carlos P." <·······@yahoo.com.ar> wrote in message
································@posting.google.com...
> Hi!
>
> I'm working in a medium-sized project using Common Lisp (particularly
CLisp).
> The code is relatively low-level with heavy use of structs and functions
> to manipulate them but almost no use of classes. There are about 15/20
files
> and, except for a few of them, the dependencies between the other are
strong
> enough to justify giving every one full access to the "public" symbols of
the
> rest in a simple way (I mean, not listing a lot of exported symbols or,
> similarly, a lot of imported ones). But, as there is also a lot of
auxiliary
> machinery (functions, macros, structs, variables, etc) that is logically
> local to each module/file I wouldn't like to make everything visible from
> outside in order to avoid name clashes. One of the solutions that came
> to my mind was using anonymous packages (automatically generated by a
macro)
> which import everything in the main package (the package where every
module
> is and from which global definitions can be accesed) without exporting
> anything. Something like a "namespace { .... }" in C++ that uses another
> namespace.
This sounds like it would work.
> Although this is factible, another solution seems more elegant
> (at least to me): using lexical scopes. So I write a simple macro similar
> to this one:
> [code and problems elided]
> What do you think? Is there a solution for this compilation problem? Or
> am I facing the problem the wrong way and should I try another approach
(for
> example, the anonymous package one)?
Ugh. This seems like a very complex `solution' that doesn't solve
the problem.
By the way, why do the packages have to be anonymous?
>
> Ugh. This seems like a very complex `solution' that doesn't solve
> the problem.
>
It does solve the problem but it introduces that subtle compilation
complication. And I think it is very simple and it feels like the
natural way to do it for me: I will use these auxiliar functions,
macros and variables only to support code in this file so I bind them
in a lexical scope. Also, as I'm working at the top level of the file
I would like to have some freedom to reorder them in order to make the
code easier to follow (anyway, this is a matter of taste and is
irrelevant to the question). Of course, the same thing could be
achieved using the packages mechanism (and more because the names
generated by the definition of some entities can't be lexically bound
-for example, structs-.....am I wrong here?).
> By the way, why do the packages have to be anonymous?
They don't need to be anonymous. But as I'm using the local functions,
macros and variables only from inside the file where they are defined
I don't want to bother about thinking a name for a package that will
never be use'd and whose symbols will never be import'ed/export'ed.
Also, if there is a significant number of files in the project I would
be taking the unnecessary risk of package name clashes or the
unnecessary effort of some kind of name administration. So it's better
if the packages are anonymous (that is, with an automatically
generated unique name). So, these are the reasons that makes me feel
that the package solution sounds more as a trick than the other. I
would use it anyway but first I want to be sure that the compilation
problem is unavoidable and that the implications of wrapping the
result of the lex macro in an (eval-when (:compile-toplevel
:load-toplevel :execute) ....) form could be prejudicial to the
performance of the compiled code (as the :compile-toplevel part of
some of the defining forms -defmacro and defvar- will not be eval'ed).
(Also, I'm trying to understand the motivations for some of the
restrictions in the use of eval-when and if these restrictions can be
safely ignored in some specific situations. In part because I'm
writing a Common Lisp compiler/interpreter and I would like to make
some compatible extensions in this area.)
·······@yahoo.com.ar (Carlos P.) writes:
> I'm working in a medium-sized project using Common Lisp (particularly CLisp).
> The code is relatively low-level with heavy use of structs and functions
> to manipulate them but almost no use of classes. There are about 15/20 files
> and, except for a few of them, the dependencies between the other are strong
> enough to justify giving every one full access to the "public" symbols of the
> rest in a simple way (I mean, not listing a lot of exported symbols or,
> similarly, a lot of imported ones). But, as there is also a lot of auxiliary
> machinery (functions, macros, structs, variables, etc) that is logically
> local to each module/file I wouldn't like to make everything visible from
> outside in order to avoid name clashes. One of the solutions that came
After having read your later posts, I still don't see why using the
package system in a straigt forward way doesn't solve your problems
in a fairly efficient way.
Just make one (or more, if it's possible to segregate functionality)
package, let's call it MYAPP-SYS, that exports all shared symbols.
Then, for each functional sub-unit (which might or might not align
with files in your case), create a separate package, which uses
MYAPP-SYS, and work in that package.
Finally, if your application also exports certain of your shared
symbols to the public, make a package MYAPP, using MYAPP-SYS, which
re-exports the public subset of the shared symbols.
I didn't understand your problem about naming the sub-unit packages.
You already have that problem today, because in some way presumably
you are naming your files. In the worst case, using a combination of
machine id and pathname provides uniquie package names, too. Though
of course, I'd rather guess that there is a saner way of naming your
packages, while still avoiding collisions.
> to my mind was using anonymous packages (automatically generated by a macro)
> which import everything in the main package (the package where every module
> is and from which global definitions can be accesed) without exporting
> anything. Something like a "namespace { .... }" in C++ that uses another
> namespace. Although this is factible, another solution seems more elegant
> (at least to me): using lexical scopes. So I write a simple macro similar
> to this one:
You have already pointed out that many things (like e.g. type and
structure defintions, special proclamations, etc.) are not lexically
scoped in CL, so this approach seems inappropriate.
If you don't want to explicitly export symbols from the MYAPP-SYS
package, you can of course programmatically export all symbols whose
home-package is MYAPP-SYS from MYAPP-SYS and/or explicitly import
those into your sub-unit packages.
I'd be interested in hearing your objections to the approach outlined
above, with some more details about your application, that are
relevant to the objections...
Regs, Pierre.
--
Pierre R. Mai <····@acm.org> http://www.pmsf.de/pmai/
The most likely way for the world to be destroyed, most experts agree,
is by accident. That's where we come in; we're computer professionals.
We cause accidents. -- Nathaniel Borenstein