TUNES: Underneath, Nearly Everything is Source

David Manifold dem@tunes.org
Mon, 22 Jun 1998 13:17:14 +0000 (GMT)


The following is quite long and not very well organized, but it should
give you a better idea of how Tunes works, inside.  I welcome your
comments.

On Mon, 22 Jun 1998, Ashley Winters wrote:

> On Sun, 21 Jun 1998, David Manifold wrote:
> > The entire system can be treated as one big expression.
> > In fact, this big expression could be written as a string.
> >
> > Anything at all that can be done in the system comes down to an edit on
> > the expression.  (Including programming, word processing, internet, etc)
> 
> If you replace 'expression' with 'mass-storage device', which could be 
> implied by 'this <mass-storage device> could be written as a string',
> it's not hard to miss the profundity of your description...

The term 'mass-storage device' loses the meaning of a high-level entity,
though.  When I think of a mass-storage device, I just think of a large
number of bits.  An expression can be understood by humans, and also acted
on by computers.  The important thing is to recognize that abstractions in
Tunes are multidimensional.  Everything in current systems is somehow
thought of as linear (streams, arrays, lists) because of habits from
low-level programming.  People don't naturally think in lines.  Tunes is
supposed to be easier to use because it mimics the way people really
think.  That means the computer (and therefore, the system programmer) has
to go out of the way to do a little more work for the user.   

> > The expression is always being evaluated.   The reason it is always 
> > evaluating is that each object has at least one element in its
> > specification, that of "existing".  So the existence of the system is
> > constantly evaluating, as long as the system exists.
> 
> That last sentence is mind-boggling, let alone the paragraph itself.
> I tried to think of something better, but just ended up glad that you were
> able to express your meaning at all. I'll think about it some more.

Here, I was trying to talk about the system "coming with its complete
specification."  The system consists of constraints, each of which specify
some property of some object.  Any property of any object has a
corresponding constraint.  Anything that is not described by a constraint,
is not in the system.  Therefore every part of the system has constraints,
and it is the systems job to implement these constraints.  Implementing
constraints is really the ONLY thing that is being done in Tunes, at the
topmost level.  Everything else is just a different kind of constraint.
More features can be added by providing the system with info on how to
implement other kinds of constraints.

Tying back to the expression idea, the giant expression is made up of many
many tiny constraints and the relationships between them.  The important
fact is that every constraint and every relationship is explicitly
described, so you can manipulate it.  

For efficiency, of course, less-frequently-changed constraints will need
to be optimized.  But, everyone uses their system differently.  All
data/code being equal, the system can dynamically optimize differently for
each person.  One of the complaints we have about current systems is the
staticity of the OS level -- not just that we can't get at the source, but
that it is optimized for a *specific* use, that may not be the use every
user needs.

> > Since the system is constantly evaluating, an edit on the expression will
> > cause changes to take place immediately.
> 
> What changes? What are the changes doing?
> 
> Let see... if programming is equivelant to editing the expression, then a
> change would be something like pressing Enter on a command-line interface?
> The immediate change taking place would be the execution of the command?

Yes.  Also, moving the mouse may update one byte that contains the current
position of the mouse.  The byte is part of the system expression, so when
it changes the entire system re-evaluates.  If nothing else depends on the
value (if no program cares where the mouse is), then all that gets changed
is the byte itself.  With the idea of constantly evaluating, I am trying
to say that Fare's "active annotations" are just the normal way of doing
everything.  A "passive annotation" does not exist, it is simply an active
annotation that isn't changing.  For example, constant integers.  A
constant integer is just a regular "integer value" that has been specified
to have no modifier method.  In order to change it, you'd have to first
change its specification (or, the objects one level above it, the ones
that describe it).  Remember, specification is done by adding constraints. 
The big expression IS the specification. 

The idea is that every part of the system can be treated equivalently (as
part of a big expression), but the parts of the expression are different
enough that you can do anything you want within them.  Looking around your
system is like browsing through an expression viewer.  Using the system
may take on many forms, depending on what interface you use.  However,
each of these interfaces (command line, GUI, or whatever) is translated
into an edit on the system expression.  So, at the topmost level, Tunes is
a high-level expression editor.

> > Any changes that are not to take
> > place immediately (or subexpressions that are not to be evaluated yet)
> > will simply be specified to not evaluate yet.
> 
> Would these be programs that are written, but not executed yet?

Yes, also programs that are not done being written (because we would not
want to try to execute them); functions that cannot be fully evaluated
because they would expand into infinite recursion; and other objects that
wouldn't make sense to execute, such as a database record.

For every property an object can have, each object in the system either
has it or doesn't have it.  So for the property of "currently executing",
a lot of things are not going to have it, but some things will.

The definition of "executing" can vary.  I actually think of a persistant
memory core as "executing" all the objects it is storing.  Even though all
it is doing is keeping them somewhere, it is still evaluating the
constraint-specification, "persist this object."  There will be different
definitions of execution for each programming language you have in Tunes.
Each program in a different language may be executing at once, but at the
topmost level, all evaluation is the same.  

To execute different languages, Tunes follows constraints to translate
expressions in the specific language into expressions in the system
hardware.  You could say that Tunes is a translator, and anything you do
in the system can be thought of as a translation.  The system hardware is
an abstraction that describes the low-level workings of a particular
machine in (complete)  detail.  Tunes, as translator, converts your
high-level expressions that you understand, by finding *equivalent*
expressions in the context that the hardware understands.  For example,
everything must be translated to machine code in order to run (normally
the job of what we call a compiler).  For Tunes, compiling is just
following constrants to adapt some expression into machine code.  The
constraints of machine code are treated by Tunes just like any other
language that it has defined.

Each subexpression of the giant expression can also be thought of as its
own language.  Or, a space.  (Fare calls them contexts)  In a language,
statements are constructed using words in the language (or, expressions
are built out of smaller expressions).  In a space, values change within
certain ranges (which is the same as a language: you can define a space to
be "the set of possible expressions available here", and an expression
will vary within that range of possible expressions).

> > The constraint to not evaluate will still be constantly evaluated.
> > (Until it is changed, at which time the expression it is referring to
> > gets evaluated.)
> 
> Uhh... I guess that makes sense, in a circularly logical kinda way.

What I said looks kind of ridiculous to me now.  In theory, you CAN think
of every specification of every object as consisting of a list of ALL the
possible properties available in the system, along with a value saying
"has it" or "doesn't have it".  However, in practice it wouldn't be very
useful.  It means the exact same thing to have a list of properties for
every object that contains only those properties that the object HAS.  So,
you don't need to say "this object doesn't execute" for every object that
doesn't execute.  It will be implied that it doesn't execute by the fact
that it doesn't have a constraint that says it executes.  However, it must
be remembered that the two scenarios I have compared here are EQUIVALENT. 
Tunes may use either representation, because it can be proven that it
doesn't matter which one.  So, in practice, the system could be actually
designed to have a list of all the possible attributes for each object. 
Then the system could simplify the specification, ignoring data it knows
it won't need.  Because of the proof system, you can design your programs
in whatever way makes sense to you.  If it is equivalent, it won't matter
to Tunes.  The idea is to make efficiency not depend on design, either. 
Programmers should design objects with their purpose in mind.  The
implementation of optimizations should be independent from the semantics
of the program.  This is just saying that you should write code without
worrying about how fast it is going to be.  If people do that, the code
will make more sense!  Then they can go back later and write essentially a
DIFFERENT program, that describes how best to optimize or implement their
earlier code. 

> > I would appreciate comments on this.. I can't make any progress on the
> > system unless there is a discussion of my posts and the ideas get refined
> > so that they are understandable.  Thank you very much.
> > 
> > Won't someone ask how reflection works??
> 
> Probably not, unless I ask. How does reflection work?

The system is BOTH one big expression, and an expression editor on that
expression.  (Concept from functional languages, data is equivalent to the
functions that operate on it, and all you need is the function.  Because
of this it doesn't matter if there is any distinguished data at all, and
everything can be a function.)  So, refining the first sentence, Tunes is
ONLY an editor for a giant expression.  The expression does not exist
APART from any functions that operate on it, because Tunes has stolen the
above concept from functional languages.  The actual expression may be
stored in any way the system likes, because you only access it through the
functions, and can use functions to convert the display of the expression
to any format you like.

So, if everything is a function, the system is a function editor.
Reflection is the ability for the function editor to edit its own
function.  All we need to do for this is put the function editor in the
system expression.  This is synonymous with saying we need to implement
the system in itself.

How does it work, though...

The function editor allows you to access any part of the system.  (Or, for
user interfaces to exist, allows the user interface to access the system
and translate it to a format for the user.)  Reflection means that
somewhere in the system is the definition for the system editor itself.
And you can edit it.  Using itself.  In Tunes, you will be able to do this
while it is running.  I guess I can best illustrate this using an example.

You have a user interface that is a mouse-based GUI.  To look inside an
object, use the left mouse button.  To bring up the specification for an
object, use the right mouse button.  "Looking inside" is the same as
descending one level in the system expression.  "Getting the
specification" is going up one level.  The current screen contains the
current level, and allows you to edit the expression here.  I am
describing the hacker interface to Tunes, which allows you to edit any
part of the system.  Many people won't spend much time here.  (Some people
won't leave :)

Now I need some example data.  It has to be a program.  Since I'm in the
GUI, I may as well write it using icons.  So my display basically looks
like a bunch of icons connected with arrows.  One of the icons is an 'if'
statement (it looks like a Y shape, to indicate branching).  I need to
change the condition for branching.  So I right-click the 'if' statement
and the conditions appear in a dialog box.  I type the new condition and
hit enter, the box goes away.  

I just changed a program.  What do people normally do when they change a
program?  They recompile it, and run it.  All I did was change one
expression, so it shouldn't take any noticable time to compile.
Therefore, the system shouldn't consider it necessary to ask me whether to
compile the tiny expression I changed, so it does it automatically.  Since
it doesn't say anything, I assume the expression is acceptable.  I just
left-click on the first icon in my program (the start icon, equivalent to
the main function) to run it.  As it runs the different icons get
highlighted to show its progress.  When it gets to my conditional 'if'
that I changed, I observe it take the correct branch and conclude my
change was what I wanted.  

How does this work inside??

When I hit enter to accept my change to the conditional expression, the
user interface tried to copy a string object (from the dialog box) to
the 'if' statement's conditional field.  But the type didn't match (the
'if' statement wanted an abstract expression, not a list of characters).

The "magic" comes into play.  Tunes has a string, that it knows it needs
to make into an expression.  So it finds a rule for translating strings
into expressions.  It finds a parser object.  The parser will accept a
string, and result in an expression.  Tunes decides the parser will
complete the translation, and places it in between the string in the
dialog box and the 'if' statement to complete the action.  Now the action
is evaluable.  At one level, we could watch the string going into the
parser, and the expression coming out.  Or we could go to the CPU level,
and watch machine instructions go by (we'd probably want to slow them
down to watch).  I'd like to go somewhere in between, and describe what
happens between these two levels.

Just above, Tunes had to translate a string into an expression, and it
found a suitable parser.  Well, this created a new expression.  The
expression says: "string->parser->expression".  Now in my edit context,
the visual program editor, knows the action is simple enough to do
automatically.  So the "string->parser->expression" expression is moved to
the "evaluate" context.  This is basically the interface to the CPU.
Anything written here is an action to be performed.  So, how to evaluate?
The 'string' is a terminal data object, so it can be evaluated no further.
'expression' (to the right of 'parser') is an abstract, *future* object.
Its value is not yet defined, but defining its value is the goal because
this is the end of the expression we are evaluating.  'parser' is a
reference to some function that takes a string and returns an expression.
It has to be looked up.  To convert from function name to function
definition, a dictionary is used.  At this point the evaluation is
remarkably like a lambda in lisp, with slight differences.

'string' is bound to the argument of parser.  This expands into the
replacement of every occurence of the argument placeholder (in C, called a
"formal parameter") with the value of the string.  Then the function is
evaluated.  I'm bored, so I won't go into the detail of how to parse a
string expression (it's easier if it's in postfix :P).  But, the parser
function gradually becomes simplified (translated according to whatever
language it is written in).  The "language" the parser is written in
consists of rules for translating that language into the CPU context.

Note: You can see here how an LLL could be defined.  A LLL is any language
that is higher level than a CPU context and yet lower-level than anyone
uses.  I can imagine several different LLLs: one for each family of
languages (a message-passing self/smalltalk like LLL, a general S-exp LLL
for various lisps, stack-based LLL like forth).  And you'd have to port
each LLL to each different CPU context, of which there is one for each
platform.  The LLL could also be a standard method of treating a single
CPU, a generalized ASM.  But, there won't be just one LLL, even on a
single system.  The translation from user representation to machine
representation will travel along a path that, after a certain point, is
definitely considered "low-level".  There can't be just one language,
because everything is made up of translation rules.  There will be
different collections of translation rules to be used at each level. 
Standards of low level representation will emerge, because they are
convenient (for the user or the computer).  Any standards will probably
change, though.

I am speaking of the HLL when it's done, of course.  Bootstrapping is
another matter which I won't think about right now.

Back to the evaluation.  I hope you see how every action that is performed
is still just 'evaluation', from high level down to the machine level.
The parser's language, written in Tunes, will specify translation rules
that can be used to translate the parser into some representation that is
CLOSER TO the CPU.  A CPU, at its basic level, could consist of a queue of
instructions from its instruction set.  Eventually everything would need
to be translated to the CPU at this level.  The lowest-level languages
would translate from higher-level languages to this level.

(If you are paying attention, you will be wondering where reflection comes
in all of this.  Hopefully the above will help you understand reflection
in Tunes more, which I am about to describe.)

At the bottom-most level of Tunes, everything is a CPU instruction in the
instruction queue.  At the top-most level, everything is an expression
waiting to evaluate.  In between is an expression evaluator, that is so
general purpose that it literally does everything.  It does this by
following translation rules to move data from one context to another.  The
data changes representations as it changes context.

Evaluation can be interpreted in the following steps:
1. Find a translation rule that meets the criteria
2. Apply the rule, resulting in a new expression.
3. If the expression is not fully evaluated, it contains more criteria and
   thus needs more translation.  If so, go back to step 1.

If you get past step 3, you have reached terminus.  The expression is
fully evaluated to the extent specified.  In the above example (modifying
a conditional expression in a program), the extent specified was to obtain
a program that could be executed at the time I press start.  Let's assume
eager evaluation, so the program I'm working on is compiled as much as
possible right away.  That means it can be pure machine code except where
it depends on input from the system.  If it works with pure low-level
abstractions (bytes), it can be 100% machine code.  Assuming the program
is non-interactive and requires NO input, it will compile all the way.
Then when I hit start to run it, the entire code can be copied at once to
the CPU instruction queue.  In practice this is rare, because not many
programs are like this, but it will happen once in a while.

Overview of Tunes at the top level:

* The main loop of the system is waiting for expressions to come in, and
evaluating them.
* To evaluate an expression, each term of the expression is evaluated.
The terms will evaluate to expressions that operate on the main one.  (The
concept of a Macro is a language that operates on another one.  The Tunes
system could truly be called Macro.)  
* The recursive evaluation of expressions results in a hierarchy of
expressions operating on one another.  The recursion stops when it reaches
an expression that can no longer be expanded, either due to lack
of information, complete expansion (to machine instructions), or
there is a constraint on the expression that says to stop evaluating at a
certain point.

Reflection means the process of evaluation is somehow described within
itself.  For the system to be reflective, there must exist an expression
that evaluates to a series of machine instructions that performs an
expression expansion.  This requires: a way to map each high level object
to the machine architecture, and a way to map each step in expression
evaluation to a simpler step, eventually mapping to machine language
instructions for that particular architecture. 

At every point in the operation of the system, everything is "high level",
that is, described as objects, which are human readable.  This means
SOURCE.  There is nothing in the system that is not source code.  The high
level is expressions, which can be in any language Tunes knows.  The low
level is machine code.  Any object can be documented (have text, or docs
in any other form, stuck onto it) for full comprehension by people looking
at it. 

> > Does anybody have any questions about the practicality of the above model? 
> 
> Practicality? How could you implement a system the way you described?
> It's a good way to picture, analyze, and evaluate a system, but I can't
> imagine how you would structure or implement one to work that way.

The important thing to remember is to keep the design high-level at all
points.  This means understandable, clear, and well-organized.  Don't hide
anything.  I have an idea how to implement the system, and I am writing
more about it to find out how feasible it is.

> > Any specific questions about "how do I do X in Tunes"?
> 
> I tried to think of something, but I can't think up a decent question. 
> Perhaps I've been brainwashed into thinking "You can do whatever you want,
> however you want, with Tunes". I could throw you a bone and ask what a
> user-interface will consist of. How do I interact with the system in Tunes?

I hope what I have written above will inspire some questions.  As for the
user interface, you can have any user interface that anyone designs, but
that's not an answer.  The interface to the objects at the highest level
consists of the axioms for manipulating the system.  This means, replacing
an entire expression with a new one, replacing part of an expression with
a new one, creating new expressions inside other expressions, and
manipulating the relationships between subexpressions of an expression. 
Well, I think any activity relating to computers can be thought of in
these terms.  If anyone has a computing activity that they don't know how
to write as an expression, post it and let's see. 

> > What we need is some vehement anti-tunesers to
> > start flaming the list.  Maybe we should go trolling on the usenet...?
> 
> That might be interesting, but you might want to setup another list 
> address so the few interested people on this list won't unsubscribe...
> 
> If some of the people on this list are somewhat like me, they let your
> messages go by without comment because they all know what they want in the
> system, and aren't interested in quantifying it like you. Perhaps they
> assume that you want what they want, and it doesn't really matter what you
> say, or how badly you say it, because they know what you really mean.

Or everybody is very busy.
Don't be busy!
One of the main philosophies of Tunes is that you don't have to be in a
hurry to get things done.  I hope to prove it.
> 
> Glad to help,
> Ashley Winters

Thanks a lot!  It was just what I needed to start writing.

David Manifold <dem@tunes.org>