More Perspective Necessary
Last night before I went to sleep I remembered a project I had done years ago that modeled a VM as an object to which one simply sent messages. At the time these messages looked like:
CPU mov eax,edx
Which naturally sent mov to CPU which compiled the mov instruction prefix at the next location in RAM, and then sent eax,edx to it which compiled the register flags (or SIB field for memory access). This was of course a marvelous design as I could write Intel assembler inline and have it compile to the right thing. Writing a self hosting system when your primitives match you hardware is trivial translation with a 1-1 correspondence.
There is then a bunch of objects on top of which on can build interesting systems which consist merely of grouping bits into different quantities: bytes, words, ints, quads, etc. Then there are objects which are interpretations on those quantities of bits: signed integers, 64 bit IEEE doubles, tagged object pointers. After those come the compositions of compositions, strings, arrays, hashed, lists, pairs, slots, subroutines. It is funny but you typically can't effectively model a machine instruction in most languages without at lease 2 if not 3 layers of abstraction. First order functions are usually at least 4 levels of abstraction out in a data model if allowed at all!
Once we escape mere aggregation and interpretation we run into the world of behavior. Objects, classes, mixins, and protocols all define layers of behavior abstraction. You can model important human concepts 5 layers deep like name, address, and phone number. Here we rely upon convention to guard against confusion. Hence confusion is commonplace. It is at this level we start talking about virtual machines as we can now model the behavior of things. People then look at the proliferation of behavioral models an conclude the only way to manage the complexity is a VM, like the JVM, CLR, SelfVM, or fill in your choice of scripting language here.
After we model sets of behavior, we start to get into higher level functions, meta-programming issues such as grammars and problem oriented languages. at this level we also start talking about a level of abstraction known as the application. Applications can be as simple as toggle this led every second to complex horrific beasts like emacs. At this point, the characterizing feature is each defines a user illusion that hides the semantic of the implementation from the observable behavior. Which is a shame, because at this level most programmers even lose all perspective of the lower levels of abstraction as well.
Next we move onto the aggregation of applications into these things we typically call systems. Systems usually consist of one or more physical machines running one or more virtual machines running one or more applications, and they all must communicate with each other to maintain the illusion. Operating systems, automobiles, and the Internet are all at this level of abstraction. Most of these systems seem too complex for any one person to understand. But that is less a matter of fact - more a matter of perspective or the lack thereof.
Beyond systems are constructs that we have yet to produce. To get there we need to define the behaviors that govern the interactions between complex systems, and work in the sort of massive aggregates that are familiar in construction. This will only be a reality when computational materials are ubiquitous. When the processing power is measured in internets per cubic meter. At that point, software engineering will escape it's infancy.
It is a matter of the technology catching up to the new idea.