[Prev][Next][Index][Thread]
Re: Sealed as Source Annotation
In article <slrn8ms4vt.9ml.mkg@asimov.lanl.gov>, mkgardne@cs.uiuc.edu
wrote:
> I would like to start a philosophical discussion of the merits /
> demerits of directly annotating the source code to seal classes (or
> make them final) versus having the annotations be external to the
> source. I am cross-posting to Dylan and Java newsgroups as those two
> languages are the only ones that I am aware of that allow annotations
> which declare that a class will not be extended.
You will know, but I expect most Java programmers won't, that a sealed
class in Dylan *can* be extended, but only within the same compile unit.
> Question: Why are internal (i.e., direct source) annotations better
> than external annotations?
I think you have to ask yourself: "what is the purpose of having these
source annotations".
They might serve merely as a form of documentation of the programmer's
intent, or they might be to enable better/faster code to be generated.
In the case of Dylan, a large part of the reason for having "sealing" is
to enable more static, faster, code to be generated. In order to do
this, the compiler needs to know about the sealing while it is compiling
any code that uses that class. The greatest effect tends to come while
compiling code that is in the same compile unit as the class itself,
which means that the sealing has to happen in either the source file
containing the class, or else perhaps in the source file which contains
the import/export information for that module.
> My experience with Java has indicated that internal annotations are a
> problem. By declaring a class final so that it cannot be modified, we
> are presupposing that the class is complete and needs no modification
> or extension. Invariably, however, the class is not complete for all
> uses and yet it cannot extended because it is final. The programmer's
> only recourse is to duplicate the functionality of the finalized class
> and then to extend it. This is clearly wasteful of the programmer's
> time, as well as system resources. (As an example, there have been
> discussions concerning the need to declaring Java's String class final
> for efficiency reasons and the attendent limit on extensibility.)
Dylan avoids this problem to a large extent by allowing subclasses to be
declared in the same compile unit with the sealed class. So it is not
that the class has *no* subclasses, but that the list of possible
subclasses is fixed and known to the compiler (and in many cases is
empty).
Thus Dylan programmers only have this problem when they want to extend
someone *else's* sealed class, and in particular, if they want to extend
a sealed class declared in the runtime library, such as <integer> or
<single-float> or <byte-string>. Of course in Java there is no question
of being able to extend "int" or "float" because they aren't classes in
the first place.
There is no real way to get around the fact that you can't extend
<integer>. There are just too many places in compiled code that the
compiler takes advantage of the fact that <integer> values are of fixed
size, are immutable, and have no subclasses thus enabling the compiler
to use a smaller and faster unboxed representation -- in Java terms, it
is these properties of <integer> that allows the Dylan compiler to
automatically use "int" instead of "Integer" in many places in the
compiled code, and automatically and transparently switch between them
on assignment, passing function arguments, putting them into collections
etc.
What Dylan *does* do, is to provide unsealed base classes for all the
sealed classes in the runtime library. So <integer> is a subclass of
<number> and <byte-string> is a subclass of <string>. The user is free
to create new subclasses of <number> and <string>. If you write code
that has variables and function arguments declared as <number> and
<string> then you can pass objects of user-defined classes into them,
but your code will be slower than if you use <integer> and <byte-string>.
> It appears to me that declaring the class to be sealed outside the
> source would allow extension where appropriate without "code reuse by
> copying". Additionally, having the annotations external to the source
> naturally leads to allowing specific instances to be sealed while
> leaving others unsealed.
I'm sorry. I don't understand your intent. Do you propose that
particular *instances* of some class might be sealed/final, but that
other objects of that class will not be? How would that work? What
would it mean? As I understand it, sealed/final is an attribute of a
class, not of an object. [bindings can also be final (called "constant"
in Dylan), but that's an entirely different thing]
> Finally, the need for annotations should
> decrease as compilers become better at determining for themselves what
> should be sealed and what should not. From my perspective, external
> annotations appear to complement smart compilers better than do
> internal annotations. (By way of analogy, most modern C compilers
> ignore the "register" keyword and determine for themselves which
> variables should be kept in registers unless a compilation flag is
> set.)
I don't think that's a reasonable comparison. "register" is a very
local property. (Can you make a global variable "register"?)
In Dylan, at least, sealing is mostly useful because of libraries and
separate compilation [1]. If you compile the whole program at once then
the compiler can see all the classes and sealing declarations are (in
theory) unnecessary because the compiler can automatically seal
*everything*. When you compile a library, however, the compiler has no
way of knowing whether users of the library might want to extend classes
exported by the library, so a programmer declaration -- visible to the
compiler at the time the library is compiled -- is the only choice.
-- Bruce
[1] in Dylan it is also possible to create new classes at runtime, by
calling make(<class>, ...). Most programs don't do this, and most
programs are written such that the compiler can prove that they don't do
it.
Follow-Ups:
References: