On design processes

Matthew Tuck matty@box.net.au
Sat, 05 Dec 1998 00:39:32 +1030


Ursula Dreier wrote:

> I feel that I should say something about what I think is important in
> a design process like that we're in at the moment. I apologize if it
> gives the impression to be rather philosophical (is that the right
> word?), but it's important nevertheless. I try to give some examples
> to clarify the motivation for all this.

Well it's probably what you meant to say but I wouldn't say it is.

> I've done some complete design cycles from systems analysis to
> implementation and customer support, and I can tell you: If you start
> implementing too early (that is, before you got everything laid out
> and double-checked in your mind), not only the time you invest in that
> early code will be wasted, but chances are that in the end even the
> quality of your design will suffer, because you will be tempted to
> reuse the code you already wrote, even if its design is flawed (nobody
> likes to start over from scratch repeatedly).

I probably come from a different angle then.  I've recently rewritten a
library I was writing, for about the fifth time, and yes, other than a
few framework methods, from scratch.  I had to to get more flexibility. 
Each time it's become better, because I understood the process better. 
Maybe I should have designed it totally first but I'm not very good at
that sort of thing.  But I think that's irrelevant, since if you do a
thorough design coding is trivial.  You often need to do several designs
and throw them out in any model.

I think you are also operating under a finite resource assumption.  In a
normal software project you have a finite set of resources, this is not
so in an open source project.  You attract programmers by your code.  If
we start writing code, we'll get the programmers who can help rebuild
it.  And we won't be reusing bad designs if I have anything to do with
it.  Even if the majority goes with the bad design, ut only takes one
small group of programmers to write the better design, and then it will
be accepted.  I think most of us can tell if a design is better, even if
we can't come up with it.

I decided we needed code after reading this:

http://slashdot.org/features/98/10/13/1423253.shtml

I think it's a powerful argument, and the many comments support it.

> If you already got everything laid out, then you should communicate that
> knowledge to us before starting to implement. This would require to
> write it down, and I bet that you'll find some more or less serious
> design flaws during that process.

I don't know how much this is compatible with open source.  Sure, design
is important, but someone will always come along with a patch to improve
it.  Of course though, good design lends itself to being extended as
opposed to modified.

Also, I know where I want us to go, I've personally currently got enough
ideas to sustain us for ten years or so, but I don't know what I want to
do first.

I think high-level and module interface design is definitely necessary,
but low-level design should be left up to the writer.  When we know more
about what we're doing, then we can afford to move away from a
prototyping based development model.

> Even if you are quite sure that you arrived at a solution that you like,
> openmindedly discussing this with others is likely to show you weak
> points that you overlooked, or orthogonal dimensions you didn' think of
> before.

I never advocated not talking about it, but you can also talk about it
with code.  Well-written code can be just as explanatory as buzzword
filled discussion, which is what we talk about seems to some of the
members.  Or rather it will complement it.

> This project has the advantage of not having a tight deadline. Although
> we should not waste time, this is (at least for me) one of the rare
> chances to do some "meta-thinking" in order to learn something about the
> design process as well as about language design and implementation (the
> latter being the most uninteresting part for me).

We're merely talking about a prototype.  It's just about dabbling in a
compiler, which will attract members.  We've got years to perfect this. 
Remember, I don't want a backward compatible language if it legacy
shackles us.  Maybe at some stage in the future we'll be ready to fix a
specification, but right now, we have an opportunity to do something
different.  By not being legacy shackled, we have the opportunity to
improve the language at an unheard of rate, for many years.

> Let me first describe how I like to do a design. Maybe you find some
> similarities or differences in your way of doing it.
> ...

I hadn't really thought about it before, but that seems like I might do
it that way.  It also seems to be applicable for both programs and
languages.

> For our discussion to be fruitful, everyone should try to determine his
> set of axioms and communicate it. This will reduce the chance of
> misunderstanding the other's implicit assumptions.

I like how you think.  =)

> For me, the most important design goals are:
> 1. Be as easy to use as possible and suited for small projects as well
> as large ones. This is because I think existing tools are not as good at
> these issues than they should or could.
> 1.1 For small projects to be feasable, there should be as little fixed
> overhead as possible.
> 1.2 For big projects, safety, scalability and clear structuring (of the
> source) are of  of premier importance.

Agreed.  I think that general language features support large projects,
while shorthands and optimisation support small ones.

> 2. It must be as simple to implement as possible. This reduces coding
> time, bugs, complexity.

I think this is true, but is not as important as most other things.  If
you can achieve this through orthogonality by all means do, but provided
you're using best techniques the compiler should do the work that
programmers would instead be doing.

That is to say, the compiler should shoulder complexity to make the
programmer's task easier.  Large language libraries are slant on this. 
In a language library, it only gets written once, reusably, rather than
a hundred times.

> 3. Be as flexible as possible.

Of course.

> Less important design goals (at least for the next future) are:

> 1. Performance (as long as the road to improvement is open)

Agreed.  I don't hold much hope of anything other than a turtle-speed
implementation for two years at least.  Being slow gives us more room to
see improvement though.  Plus having a better language to bootstrap with
allows us to easily write the algorithms necessary for efficient
compilation.  If we include inter-module optimisation, we'll certainly
have a leg up on other compilers though.

> 2. Portabality (presents no headache to me, given a proper design)

I think the design should be portable from the start.  I don't see any
place, at least in the language proper, where portability is not easy to
achieve.

> 3. Being useful for each and every purpose (if someone wants to write
> real time apps, action games, device drivers, they should use other
> tools).

To a degree that's true.  I don't profess to know much about real-time
applications other than that they are programs that need to operate
within time-constraints, which is not the same as making apps that are
efficient.  I'm not concerned in the slightest with this at the moment.

Device drivers are probably more low-level C style, but then I don't
know much about them.  I don't see any reason why action games couldn't
be written in Ultra in the long term though.  My long-term plan is to be
better than C, so why not?

It probably goes without saying general language features can subsume a
lot of different types of applications, but also domain-specific
user-defined shorthands could help bridge the gap.

I'm half in favour of this statement.  On one thing there are things I
don't want to have to worry about (I'll admit to being apathetic about
parallelism beyond threading, which is probably a bit dumb), but on the
other hand I believe in the ideal of a general purpose language.

> 1. Everything that uses memory should be contained in typed objects, so
> it is subject to garbage collection. For optimal results, that includes
> code (be it source code, executable, byte code, or whatever). Maybe an
> exception can be made for the very kernel (VM / memory management / boot
> loader) because that won't ever be discarded.

If you make that exception I think you're back at the standard system. 
But anyway, you don't have to garbage collect everything as long as it
looks like you are on the language level.

> Each object that is accessible as a separate entity should be contained
> in its very own memory block.
> To clarify this: If you can store a reference to an entity at source
> level, that is an object. If you can access it only using some built-in
> method, that need not be an object. From this follows: References always
> reference objects, never parts of them.

There's no need to restrict this sort of thing to built-in facilities -
general optimisation algorithms can do things wherever possible.

> These restrictions always in mind, there still can be a "compressed"
> from of a general tree residing in one object. This is useful for
> semicompiled "threaded" code (to be executed by a VM), which consists
> primarily of object references with some directly interpreted parts in
> it (jumps, for example).

I've thought of the idea of defining your own heap over the top of the
main heap - you could compress or compact using this.  You would have to
implement your own GC though.

> Object layout is most important: What cannot be assembled within the
> assumptions made regarding memory layout, cannot be implemented. It will
> have to say more on that in another thread.

Be careful here not to restrict your layout based on efficiency
reasons.  You could provide a more efficient version for what the
optimiser and code generators determines is allowed, and use the less
efficient one where it is required.  I'm not saying you've done this,
but bear it in mind.

> 2. I want to do every task at the highest possible abstraction level.
> For example, not hardcoding the operator list, but rather keeping it in
> some list object, together with binding powers and the like.

I'm not sure what you're referring to here.

> Objects should be describable in terms of theirselves (recursively) as
> much as possible.

Not really here either.

> I really like the idea to store source code in an already structured
> from, as precompiled as possible without reducing the freedom of the
> programmer more tham necessary.
> This will help to keep the kernel small and clearly laid out and reduce
> the overall amount of code.

I haven't really thought about it in these terms before.  How will it
keep the kernel small?  The VM kernel or the compiler kernel?

> 3. Be as independent of other software systems as possible, especially
> at places as crucial as the kernel.

This sounds like portability through modularisation.  Is that a fair
assessment?  Long-term yes, but we might have to concessions to this in
order to speed up development.  Hopefully not.

This also seems to say we shouldn't use things like compiler
generators.  Maybe I'm reading more into it than there is.

-- 
     Matthew Tuck - Software Developer & All-Round Nice Guy
                              ***
       Check out the Ultra programming language project!
              http://www.box.net.au/~matty/ultra/