|
|
Home
>
3. concept
>
3.2 libraries and modules
|
Previous
Next
|
|
|
|
|
|
|
|
|
|
|
|
Natas
|
What I don't understand is why, in all the code examples
I see, the first line
in the library definition file is Module: dylan-user
|
|
Rob
Myers
|
It's saying where the contents of the file should go, rather than creating
anything. Dylan-user is already magically defined, and code needs to live in
a module, so declaring code to be in dylan-user avoids problems with having
to declare libraries and modules before you can declare libraries and
modules.
> I don't understand why the Module: statement is needed, if libraries
> are at the top of the hierarchy, i.e., libraries contain modules.
> Doesn't this statement say that the time library is in the
> dylan-user module?
I should know this one. :-) I think it's also because code has to be in
modules. :-) Hopefully someone can give a better answer than this.
|
|
|
Bascially, the *text* of a Dylan /program/ consists of zero or more /source
records/, each of which contains zero or more /top-level forms/. A top-level
form is essentially a "define something ... end" macro call or an expression
(including, for example, function calls or "begin ... end" statement blocks).
The only thing you can't really have at top level are exception handlers.
Definer macro calls *are* conceptually executable code but (at least for
libraries and modules) they're always executed at compile time, and they're
pretty much independent of order (in theory -- Your Compiler May Vary).
The "Module: foo" line is not part of the Dylan language itself, but of
the
"Dylan Interchange Format" (also defined in the DRM) for storing Dylan in
text files. Each text file constitutes a source record. If you had a
development environment like Apple's Technology Release, source records
might be in a database, which would store and display the module of that
source record in some other way.
The compiler has to know which module a source record belongs to, so that
it knows which bindings are visible and where their definition comes from.
The *only* /special form/ is "define macro", which can't be renamed or
shadowed.
That describes the text of a Dylan program but not the library/module/binding
*containment*. Libraries are compile-time entities which are created by calls
to "define library" (or, to any macro whose expansion calls that, but I'll ignore
that for now). The library itself lives in a global namespace, outside any
modules, but the *definition* has to happen in a module where "\library-
definer" is visible.
(Similarly modules are created by calls to "define module". Which library
they exist in just depends on which other source records they're compiled
with -- there must be exactly one "define library" for it to be a valid Dylan
program. The module might be exported by the library, but it might not -- it
might be a utility module imported by other modules in the library.)
The dylan-user library exists so that you can create a source record in which
"\library-definer" is definitely visible; in that source record you can define your
library.
Theoretically you could write something like this:
------------------------------------------------------
Module: my-module
define library my-library
use dylan;
end;
define module my-module
use dylan;
end;
------------------------------------------------------
and then require
the compiler to prove that the calls to "\library-definer"
and "\module-definer" here are really calls to the macros with those names
exported from the "dylan" module. However, that's (even) more hassle for
compiler writers and you could probably increase the complexity of
"circular" examples like these until some compilers could cope and others
couldn't.
Lastly, the reason
for not doing "use dylan;" implicitly is that you might
want to use an implementation-specific library/module which exports all
the bindings of the Dylan library/module, plus some of its own. FunDev
has "functional-dylan" as well as versions of "dylan" which do some magic
on "\+" etc. to support generic arithmetic; both FunDev and Gwydion
have "common-dylan".
|
|
Gabor
Greif
|
Let's put it this way: Top-level-forms (which generalize "code") are only
allowed in modules. Since modules (and their hosting libraries) can only be
declared as a top-level-forms, there is a need for some pre-existing module
that allows this. Dylan-user module fullfils this purpose.
|
|
|
|
Kim Barrett
|
The library is by definition the unit of compilation for
Dylan.
|
|
Bruce Hoult
|
I don't understand the rules for the "define library" and "define module"
declarations themselves. Why aren't the "define module" declarations
nested within the "define library" ones?
Does the scope of the "define library" extend to the end of the file? Is
it possible to have more than one "define library" declaration in the
same file?
|
|
Kim Barrett
|
They are separate so that you can easily have two libraries which
include the same module.
Say there are two libraries for two different products, and both use the
same utility module. That utility module could be packaged in a
separate third library which is used by both of the first two. But that
might increase opportunities for configuration problems, and might
decrease optimization opportunities due to separate compilation of the
product library and the utility library. Kind of like the choice between
dynamically linking or statically linking a library. That utility module
could have its module definition copied into the two product libraries,
but that would likely lead to the usual multiple copies of the same
source code problems, so was not seriously considered as a solution
to this problem.
|
|
|
|
Greg Sullivan
|
One shortcoming of Dylan's module system is that you cannot
be more specific than names -- i.e. you can't specify the type
signature of methods or generic functions at the module level.
At least Java interfaces allow you to specify the names _and_
types of methods that must be implemented.
|
|
Kim Barrett
|
Speaking as one of the designers of the existing module
system, I strongly disagree with you on this. It was in fact
completely intentional that Dylan module definitions *don't*
contain such information.
Dave Moon explained it well, in a long-ago (3/3/94) email
discussion:
Dylan does not require programmers to make a textual copy
of portions of the definitions of items exported by a module, like
a C header file or an Ada package declaration.
In my opinion Dylan modules still have a well-defined interface;
the difference is that we assume there will be the obvious
development tools for printing out that interface, instead of
requiring programmers to pretend to be tools and copy text from
one file to another. (Except of course Dylan still requires
programmers to copy the -names- of the exports from their
definitions into the define module statement. We haven't
thought of a good way to avoid that.)
|
|
Greg Sullivan
|
Having type signatures as part of the module system would
enable better cross-module type checking and better
optimization.
|
|
Kim Barrett
|
Having type signatures as part of the module system should do
nothing to improve cross-module type checking and
optimization.
The development environment and delivery model assumed by
the existing specification of module defining forms is that part of
a library that one can compile code wrto and link to is some
information provided by the compiler when it processed that
library. One of the things that should be in that information is
the stuff needed to compile more efficiently references to the
library's bindings. Type information is only one of many bits that
ought to be placed there, including such things as inlining
information, sealing, expected code size (might be used for
automatic inlining decisions), expected runtime cost (again
useful for inlining), and so on. Much of that can't reasonably be
generated by the programmer, and really needs to be done by
the compiler. Given the existence of this repository, it seems
pretty silly to make the programmer maintain two independent
copies of the type information when the compiler could just as
easily generate the needed information by processing the
definitions instead of comparing the two copies and complaining
about the occasional mismatch.
|
|
Hugh Greene
|
An extension I'd like to see to Dylan is the addition of type
information in the export list of a library. The actual
implementation types might be more specific than those in the
module exports; the export types would allow the compiler to do
more optimisation while still allowing the implementation to
change. It'd be half-way between the current "loose" and "tight"
binding supported by FD
|
|
|
|
Eric
Gouriou
|
I realize that the "define module" macro should not be
extended. I believe that since it is the level where
language bindings are exported, modules grouping
modules (ie modules replacing the current libraries)
should be forbidden.
|
|
Hugh
Greene
|
This is, I think, why modules don't currently nest. Also,
names of nested
modules might clash with binding names in the same
module, which would be
really irritating.
|
|
|
|
|
|
|
|
|
|
Nick Kramer
|
No such separation via the module system (In my opinion, this is a
gaping hole in the language). The module system is pretty good when
it comes to managing namespaces, but it has no concept of contracts
or interfaces. You can export the class, but you can't specify which
generic functions it has methods for.
|
|
Rob
MacLachlan
|
Nick's view is not the consensus in the Gwydion group. It's true that
the module system doesn't seperate interfaces and implementations.
Instead, it seperates arbitrary chunks of Dylan code, some of which
may be interfaces and some of which may be implementations. In
Dylan, an interface is primarily defined by a set of "define generic" and
"define abstract class" forms.
|
|
P T
Withington
|
I recall the recommended practice to have an interface module that
creates the names and then any number of implementation modules
that will assign definitions to the names.
Module: dylan-user
// Library definition
define library say
// Interface modules
export say, say-implementor;
// Substrate libraries
use format-out;
use dylan;
end library say;
// Protocol interface
define module say
create say;
end module say;
// Implementor interface
define module say-implementor
use say, export: all;
use format-out, export: all;
end module say-implementor;
// Implementation module
define module say-implementation
use say;
use dylan;
end module say-implementation;
|
|
|
|
|
for Chimp, I mostly
just import the low-level, and export it
under my high level names:
define module chimp-MPI
use chimp-low-level,
rename:{
%chp-recv => MPI-Receive,
%chp-recv-nb => MPI-NB-Receive,
... }
export: all;
end module chimp-MPI;
|
|
Harley Davis
|
There already is a way to do what you're doing in Dylan: Just
define a new variable which has the value of the previous one.
For instance (excuse any syntax errors):
define variable MBI-Receive %chp-recv;
define variable MBI-NB-Receive %chp-recv-nb;
So I don't see that this particular feature gives Dylan any
extra power.
|
|
Andy
|
In what way is introducing a new variable the same as
introducing an 'alias'?
|
|
|
|
|
|
- namespaces in Dylan, and their applicable
scope;
|
Namespace
|
Scope
|
|
library
|
global
|
|
module
|
per library
|
|
constant or variable
|
per module
|
|
symbol or keyword
|
global
|
|
|
|
Johan Ovlinger
|
I assume that there are scoping constructs so that two
different method using the same naming don't interfere
with each other?
|
|
Scott McKay
|
Of course, this has all been done in Lisp (CLOS) and
Dylan. Generic functions are what you use to extend
functionality, packages (or modules) are what you use to
prevent clashes.
|
|
|
|
Patrick D.
Logan
|
Many of the C++ libraries I can think off hand use prefixes
in their names. This is essentially an ad hoc solution.
(What if two vedors choose the same prefix?) And not very
pretty.
[...]
|
|
Kim Barrett
|
This problem was considered and dealt with in the design of
Dylan.
Libraries are the place where the regression is terminated.
- A provider's library contains a define
library form that
names the modules exported from the library.
- A client's library contains a define
library form that
names the libraries that are used, including the
modules that are imported.
(With module renaming allowed, just like variable
renaming when importing variables into a module, in
order to resolve name conflicts).
While a define library form contains a name for the library,
that name isn't actually important. Specifically, the library
names in the client's imports don't really have anything to
do with the provider's library name. Rather, the association
between library names in the client's imports and the
provider's product is made in some extra-lingual
implementation-defined manner. This mechanism can't
really be portable because it necessarily deals with such
issues as filesystems and their naming conventions (we
could have bumped the problem up to an implementation-
defined mechanism for mapping from "generic, portable"
filenames to actual filenames, but didn't), and so on.
We intentionally made no attempt to specify the specifics of
such a mechanism in order to provide as much latitude as
possible to implementors in this area. However, just to
make it a little clearer, here are sketches of a couple of
possible ways it could be done:
- Edit a standard configuration file
associated with the
client library, specifying the mapping from library
names used by the client to the filename where the
used library is found.
- Drag and drop the icon for the used
library onto the icon
for the client library.
All of this is in the DIRM, though perhaps not as obvious as
it could be.
|
|
|
|
|
|
|
|
|
|
Harley Davis
|
I think the name conflict problem is a fairly minor issue
in the
overall scheme of things.
The fact is that the C & C++ library market has not
suffered at all
from this problem; each vendor chooses their ugly prefixes and
life goes on.
So I don't think this aspect of Dylan's module system
(or C++
namespaces) will have very much impact on Dylan's (or C++'s)
success or lack thereof.
|
|
Wendell Berry
|
Is that the ultimate criterion for having them in the language?
Modules put namespaces (and name choices in general) in the
consumers' hands rather than the providers'.
In Dylan the consumer can choose which modules to use (even if
they have the same module name) and which variables to use
(even if they have the same variable name). The consumer can
rename any variable to resolve a conflict or even just to be more
meaningful in that consumer's context.
Why have to deal with ugly prefixes, that ultimately don't resolve
all possible conflicts, when there is a module system that is
- easy to implement; and
- would resolve all conflicts at the
disgression of the
*consumer*?
|
|
|
|
Jason
|
Is it non-Dylan thinking to wish there was access to the
namespace?
|
|
Mark Craig
Chu-Carroll
|
Two things:
- Why do you *want* access to the namespace?
I'm used to be able to much around with symbol bindings in
CommonLisp, but I've needed to in Dylan. It's a *lot* cleaner and
safer to just use a table.
- Names don't exist at runtime in Dylan.
If you give up that distinction, and require the implementation to
maintain symbol tables at runtime, then you're crippling a huge
number of useful optimizations. It can also get terribly confusing,
because
there is *no* global namespace in Dylan.
- The names of variables depend on
the module in which they're
used.
- The names of modules depends on the
libraries in which they're
used.
- And the names of libraries depend
on the environmental context
in which they're compiled!
In most Dylan implementations, a libraries name can be altered by
the environment. When you declare a library "A" which imports a
library "B", the binding of the name "B" to a particular library is
performed by the environment. There is no guarantee that the library
that you use as "B" was compiled as "B"; it may have been compiled
as "HickeldyGobblyGook".
|
|
|
|
|
|
|
|
|
|
P T
Withington
|
I recall the recommended practice to have an interface module that
creates the names and then any number of implementation modules
that will assign definitions to the names.
- a generic function is a separate
protocol
To permit separate libraries to add methods to a Dylan generic
function, the module defining the protocol (that is, the modulle
defining the generic function) needs to be first
Module: dylan-user
// Library definition
define library say
// Interface modules
export say, say-implementor;
// Substrate libraries
use format-out;
use dylan;
end library say;
// Protocol interface
define module say
create say;
end module say;
// Implementor interface
define module say-implementor
use say, export: all;
use format-out, export: all;
end module say-implementor;
// Implementation module
define module say-implementation
use say;
use dylan;
end module say-implementation;
|
The definition clause can be used to describe the following roles.
|
Role
|
Example clause
|
|
interface
|
// Interface class
create <time>;
// shared protocol
// via a re-exported interface
use say, export: all;
|
|
client
|
// Substrate module
use dylan;
|
|
implementation
|
// Interface module
use time;
|
|
implementation and interface
|
// Interface protocol
export say;
|
These role of clauses get used to build up protocol interfaces, implementator interfaces,
and
implementation modules
|
The roles of module
in a library definition
|
Proposal
|
|
Module: dylan-user
// Library definition
define library time
// Interface module
export time;
// Substrate libraries
use sixty-unit;
use say;
use dylan;
end library time;
// Interface module
define module time
// Classes
create <time>,
<time-of-day>,
<time-offset>;
// Shared protocol
use say, export: all;
use sixty-unit,
import: { encode-total-seconds },
export: all;
end module time;
// Implementation module
define module time-implementation
// External interface
use time;
// Substrate modules
use sixty-unit;
use say-implementor;
use dylan;
end module time-implementation;
|
define library
export interface time
prepare implementation
end library
define interface-module time
create <time>,
<time-of-day>
<time-offset>
shared-protocolls:
say, export: all;
sixty-unit,
import: { encode-total-seconds },
export: all;
end interface-module
define implementation-module time
supported-by:
sixty;
say-implementor;
dylan;
end implementation-module
|
|
|
|
|
|
|
|
|
|
Eric
Gouriou
|
I will never replace/upgrade those libraries individually, so having
them as DLL is useless to me (and I indeed would appreciate
having an option to compile those multiple projects into just one
executable, taking advantage of the added optimization
capabilities).
|
|
Chris Double
|
If you're not going to use the reuse the libraries or replace/upgrade
them individually, why make them libraries? Why not just make
them modules within a library? This would also take advantage of
the added optimization capabilities.
|
|
Eric
Gouriou
|
Indeed. And that's exactly why I want the module/library concepts
to change, because these "define libraries" group my modules in
well separated sets of functionality. If I were to put all the modules
in one or even two libraries, the result would be a mess.
A typical example is this "Ego-Extensions" library. I want to keep it
as a library, as most of my projects import it. But I do not want to
have it as a DLL, as it is mostly a bunch of miscealleanous helper
functions/macros that should be inlined and/or dead-code
eliminated in each of those projects.
|
|
|
|
|
|
|
|
|
|