Systems Opinions (David Jeske)
Message-ID: <19971223212528.18418@home.chat.net>
Date: Tue, 23 Dec 1997 21:25:28 -0800
From: David Jeske
To: Jeff Thompson
Subject: Re: Security stuff
Content-Type: text/plain; charset=us-ascii
X-Mailer: Mutt 0.88e
Content-Length: 9079
Lines: 161
I wanted to separate this from that last long email because I think it's
fairly important and alot of that stuff was just background rattling. Much
of this is about run-time systems. It'll sound like alot of language
stuff, but I think it's really relevant to operating systems.
The most relevant decisions I've come to support recently are:
1) strict non-mutable interfaces
2) NO mix of code-reuse and object typing
3) TRULY abstract interfaces
4) some kind of "correctness" verification (i.e. a strict CRC policy)
----- elaboration:
1) Microsoft's COM is the only system I know of using this concept, and
I think it's a MUST. There is some good and some bad of COM, but this idea
is great: An "interface" is something you define uniquely in the universe
and which must never change. Basically, an object in COM can have any
number of interfaces. You can either be handed, or query for, an object,
and then ask the object for an interface, or you can query the system to
find an interface for you.
At any rate, this solves "the big problem" with dynamic systems as I see
it. I've always loved dynamic programming systems for their flexibility,
much of which comes from polymorphism, much of which comes from having a
"real" runtime system. However, this creates lots of problems, most of
them coming from the same place as the flexibilty. Namely, that object
interactions are based completely on IMPLIED contracts. Also, in most of
these systems, some implication is made that method names which are the
same hold some kind of similarity across objects. I don't like either of
these concepts.
Hardening interfaces makes the contract to suppily a specific service
EXPLICIT. It also removes the implication that similar methods perform
similar functions. If you want to provide a service, you just support the
interface which defines how that service works. Some concept of
"translation" between similar interfaces can easily be put in place to let
similar (but distinct) interfaces cross-operate.
Many people who I know who defend pure dynamic systems do so under some
guise that you get added utility out of this "loose coupling" however,
programming systems have no tolorance. Computer execute discrete
instructions, and in the end, something either works or dosn't work.
(unless you create another paradigm within the computer of course)
This kind of interface based system can get exactly the same dynamic
functionality as a dynamic language. Take, for example, the case of the
polymorphic list. When you put Objective-C or Smalltalk objects into a
list, they go in because they are objects, and they come out because they
are objects. When the receiver gets the object out, it can either
explicitly ask the object what it is, or it can just start using it and
see what happens. Either way it's going to ask it to do something, and the
only way the pre-written code is going to know what to do with the object,
is if the interface it conforms to was specifically in mind. In the
interface based system, you can put any object into a List. When the
receiver pulls something out of the list, it gets the same dynamic
functionality of being able to ask it what it can perform. However, after
it asks this question, it knows that the object is explicitly declaring
it's contract to perform the interface's service as defined by the person
who wrote the interface. If the provider does not do this, it's his fault.
Certainly current API mechanisms are not good enough to formaly specify
that interface to a degree that bad code can be prevented. However, it
becomes much easier to create conformance tests which check for
conformance to the _known_ interface rules.
2) NO mixing of code-reuse an object-typing
This (IMO) is Java's biggest mistake. Think of the whole "class" vs
"interface" thing. I think Java interfaces are great. You ask an object if
it "implements" something, and it tells you. That's exactly what you want
to know. However, imagine if something is done as a class, instead of an
interface. A view system for example. If you ask something what it's class
relationship is, it implies a path of code-reuse which has NOTHING to do
with whether or not it performs the requested function. More importantly,
if you create an object which is fully capable of performing the requested
function, there is no way for you to get code to believe it can, because
they will be asking about it's code-reuse history (i.e. superclasses) not
about it's interface conformance.
Thus, there should be no mixing of code-reuse and interfaces. COM
provides a concept of code-reuse better designed for interface based
objects called "aggregation" where you basically subsume objects into
yourself. People don't really understand it, and it admittedly has a few
quarks. However, it dosn't just solve this problem, it also solves the
tragic "fragile-base-class" problem.
3) Truly ABSTRACT interfaces
I think there is about as much chance of this happening in software, as
there is that the world will suddenly become flat. However, I've come to
realize that all that talk about top-down back in school wasn't just
right, in fact, it was so right that I don't think people realize how
right it is.
My current favorite example is the standard file directory concept.
Applications on every platform are plagued with dependencies about where
their datafiles are stored for one reason. When people wrote the software,
there was no abstract API which they could use to find their datafiles.
Imagine if application authors could have just called
"open_my_datafile_called("boo.tiff");" instead of
"(open("/usr/local/lib/emacs/boo.tiff");". The reason they couldn't do
this is because there was not an abstract API for doing it. However,
ANYONE doing something new will not have an abstract API for doing it. I
think it's a fundamental flaw in computing systems that it's a difficult
thing to define an interface and create a supplier of that interface. In
traditional systems, it's the shared library.
In current technology terms, what I'm talking about is writing software by
defining an interface for everything you need to do, and exporting that to
the world as an interface. (which today must be done with a shared lib)
Imagine an email app, instead of just writing a terrible editor for the
email app. One could just define the interface (no matter how simple) and
then write that terrible editor to provide that interface. When you ship
the software it will work exactly the same way. Except that it's just a
matter of an interface translator, or a new custom editor supplier, to
allow other worth editors to be dropped in.
I want application systems on a desktop OS which work more like the Newton
or the Pilot. Where the system knows which applications you have, and
applications have ways to get to their datafiles. However, today operating
systems are riddled with "strap on solutions" which provide these
capabilities add-hock on TOP of existing systems. Everyone has their own
"solution" to this problem in the form of an install program for windows.
Most of them to the right thing about informing the registry and making
sure you can use "add/remove programs" to uninstall the app. However,
there are always problems. If ONE person had just written a shared
installer which would actually deal with putting the app into the system,
and then come up with a backward compatibility solution for old apps to
fit into the system (even if poorly), the problem would be solved.
Of course, you have to also remember that people need ONE way and ONLY ONE
WAY to do something in a software system. Otherwise, they'll choose the
wrong way. So after this shared installer was written, the place these
apps files were installed would have to be hidden, sheltered, and not able
to be found via any API except for the "get_me_my_files()" function or
whatever. (i.e. while the app was running)....Otherwise there would surely
be someone who let their app be installed vvia the universal installed,
and then found out the full path to their app and started mucking with
things.
4) Some type of strict "correctness" policy
Today systems, particularly those with poor file systems often have
errors which creep into parts of the system. Which is not so bad as the
fact that they go unnoticed. How many errors creep into windows system
DLLs before there are just weird quirky things going on in the system
which you end up having to "reinstall over" to get things back to a
working order. Plus, you never knew what was broken.
I want a system which actually remembers CRCs for things, and can really
tell me when something was corrupted. It dosn't have to tell me the
instant it happens, and it dosn't have to stop that component from being
used in any form. It just has to tell me that it's broken, so when I am at
a convinent point, I can pull out the correct install CD, and use that
system provided install/repair tool to have it refresh the installed
component from the install files.
--
David Jeske (N9LCA) + http://www.chat.net/~jeske/ + jeske@...
David W. Jeske