Computers are all about abstraction, you can take a computer at look at it from many levels of abstraction, right now I'm typing in open office which is an abstraction, it allows you to concentrate on composing your document without worrying about the details of how your keyboard works, how to display your document to the screen, how to store it in memory etc. Indeed if I had to worry about all these things I'd never actually be able to write a document using my computer it'd just be too complicated. Yesterday I was at a lecture which was about basic computer architecture. We were looking at a computer at a level where it's a collection of hardware devices, a processor, memory etc that we can control using machine code. Sometimes you have to think of a computer like this (such as when you're writing a compiler, an operating system or a device driver) and sometimes you have to think of it as a glorified typewriter (such as when you're writing a document) thinking about a computer as a glorified typewriter while you write an operating system on the other hand just doesn't work. You have to match what you're trying to do to the correct level of abstraction (or indeed to several levels of abstraction).
We use abstraction in programming as well, APIs such as OpenGL and DirectX allow us to specify polygonal data, states and shader programs without needing to worry about or know about how the underlying hardware actually works. We create new abstractions using constructs such as classes in C++ (e.g. We might abstract an API like DirectX using a class so we can think about rendering in terms of specifying materials and meshes, instead of polygonal data, states and shader programs). Abstraction is a very important concept without it we just wouldn't be able to use computers (just imagine having to think about the quantum behavior of a single transistor in your CPU when you want to write pong).
In electronics you have components such as transistors, capacitors, resistors, integrated circuits etc. All of these are 'black boxes' you know their inputs, their outputs and how they behave, but you don't actually know how they work (or at least you don't have to know how they work in order to use them), they abstract away the details and allow you to concentrate on your circuit. When you're designing a complex piece of hardware you're going to design it as several different levels of abstraction, you're going to have an overall block diagram showing how high-level systems connect to cover, for each high-level system you're going to have further block diagrams. At some point you're going to have a circuit diagram, if you're making an integrated circuit you'll eventually have a diagram showing exactly how every transistor is implemented and connected in to the entire system. You're designing at several different levels and which level you're designing at depends upon the task you want to accomplish.
Now if you take a look at the source code of a program, all you get is text, the text defines the entire program and when you're working on the program you're going to be working with text. I think the process of creating a program should be more like creating a piece of hardware where you have many levels you can look at the system and alter the design on. Currently with program design you may well draw UML diagrams etc to aid in the design but once that's done you translate your diagrams to textual code (either manually or have some tool do it for you) and that's that. I propose that all the diagrams you might draw in the design stage should be coupled far more tightly to the actual program. So you could look at a diagram showing how all the systems in your program are connected together and rearrange it however you want and that change would automatically be reflected in the actual program.
Going back to electronics if you draw a block diagram of a system you may well have the blocks representing specific ICs you're going to have in your circuit and lines connecting them to show how they communicate with one another and these lines can directly translate to wires within the circuit connecting the pins of different ICs together, that is the abstract design I draw trivially converts to the implementation. Now back to programming, lets say I'm drawing a diagram of the design of my new super-duper next gen engine. I may draw a line between the block marked 'scene graph' and the one marked 'renderer' and then I could write something next to the line such as 'scene graph sends potentially visible objects to graphics queue' however this line and sentence does not trivially convert to the implementation. Now imagine I have in the block marked 'scene graph' a bunch of fields (basically a load of names separated by lines) one of which is 'visible objects' and in the renderer block I have a field marked 'render queue' I could now draw a line between visible objects and render queue to symbolise that the visible objects are moved to the render queue and this could be trivially mapped to the implementation (OK it's pretty much exactly the same as just writing a sentence next to the line, however it's far easier for a computer to interpret and for a human to see at a glance what's going on) as I'm basically saying move data from here to here. What you need to do is define the fields correctly as well as a way to transfer data.
Think of a programming environment where you build programs using blocks like I described as above, you could use them recursively, that is you could have a single block at one level that is defined using many blocks on another level. The blocks would all have inputs and outputs (like the fields mentioned above) that could be connected to represent data transfer between blocks. Also I wouldn't remove actual textual code this would be seen as the lowest level to implement a block in. I would however get rids of text files, the program would be stored in a format appropriate to the way we're representing the programs (probably some kind of complex graph).
This kind of approach is interesting for multi-threaded programming as well. Seeing as the program wouldn't actually define a strict execution order a scheduler would be free to run the code for blocks whenever it is deemed appropriate so you could have several blocks executing in parallel, the connections and data transfers between the blocks are explicit and as such the scheduler could take them into account to avoid concurrency problems.
I've been thinking quite a bit about this over the past few days and I'm constantly coming up for conflicting arguments for ways to do various things and as such the above text is probably pretty confused though hopefully you can get the basic idea of what I'm getting at. I may start playing round with actually creating a programming environment like I described above during the Christmas holidays to see if I can actually get anywhere with it.
Oh and if you've actually read this entire thing could you leave a comment telling me what you think even if it's just a single word, thanks. [smile]