Tuesday, July 26, 2005

Computer Interfaces III

(Third part of a series inspired by experiences in amateur tech support and the endless stream of articles yapping about how the time for desktop Linux is either never or next year. Yet another one was released recently.)
In the first part, we learned that people tend to work on the terms of machines, with the simple example of the telephone.
In the second part, we learned that computers blindly process numbers (and only numbers!) without understanding anything about their meaning or context. The corrolary is that if there are only two entities in a room, you and a computer, there is only one intelligent being there, and it better not be the computer.

Last time, I showed that a direct interface to all the machine's capabilities would be impractical. But, something's got to tell the machine what to do! Before we can discuss end-user interfaces, then, we'll have to digress into what the computer looks like to a programmer, (hopefully) in a simplified enough sense that an end-user (read: normal person) won't run away before he finishes reading this sentence. Briefly, this is the story of how the computer went from being a machine that could only be used by specialists to one that could be used by everyone.

The machine itself understands some numbers as operations, some numbers as — well — numbers, and some numbers as references to places it can find other numbers (for example, memory!), etc. So, the second-lowest level interface would be a set of word mnemonics that a human might understand that directly correspond to the numbers that are machine-readable. We call that assembly language. In assembly language, there is a one-to-one correspondence between a "mnemonic" and an operation. So, instead of:
1 45 62
I can say:
add var, 62
(note: I don't think this is an actual correspondence on any processor, it's a just a simplified example).

Now, let's say that someone develops a way for the computer to convert the "assembly language" mnemonics — which are gobbledygook to the computer — into the machine language that the computer will actually be able to process (incidentally, that type of program is called an assembler). Now, nobody will ever have to instruct a computer in machine language again. Hooray! The machine's interface has been simplified one step.

Notice that the mnemonics for the operations are now somewhat understandable, but, there's no intuitive way to connect any set of operations to the kind of result one expects out of a modern computer. Even the simplest of "operations" in human terms (say, asking the user to choose two numbers, adding them together and printing the result to the screen) may be hundreds or thousands of lines of assembly language code, meaning hundreds or thousands of machine instructions are executed in order to accomplish the task. Further, programming in assembly language requires very detailed knowledge of the computer internals. And that applies to trivial tasks, like writing one letter on screen. The assembly programmer/user needs to know exactly how the processor talks to the screen and what the screen (another very stupid machine) understands.

Before we can make computer programming a more general task, we'll need two more additions: high-level libraries and high-level languages. Most historical or semi-historical accounts would put high-level languages first, but, for my purposes, I think it's better to start with the idea of libraries. Let's say, for example, that I know how to write a character to the screen and you don't. So, I write a procedure that will write a character to the screen. Once that's done, I pass it to you and tell you how to use it. From then on, you no longer have to worry about the details of writing characters to the screen. You may then extend that concept, and write a procedure to write a whole string of characters to the screen. Similarly, let's say I don't know the details of how the hard disk hardware works, but you do. You can write me a procedure that will do it for me, and I won't have to worry about the hardware details. You can think of it as handing over a job to a specialist. It would be as if DW tells me to fix the air conditioner, retile the bathroom, and reside the house. I may not know how to do any of these things, but, I can call someone who does. Either way, from DW's perspective, the work was done. In the analogy, DW is the programmer who wants to write something complicated. I am functioning as a high-level library, and each contractor is functioning as low-level library that runs sets of machine instructions. DW and I still don't know how to retile the bathroom, but we got it done anyway by passing on the work to someone who does. That's how programmers work. They take basic libraries that are provided for them, and write other libraries that make use of those, and so on. At each step, we gain the ability to do something we couldn't do directly before, and we also lose sight of the details of how it's done.

So far, we've assumed that everything is written in assembly or machine language. But, let's say I could write a program (in machine or assembly language) that would translate even more-human-readable text into machine language. A construct like:

int a = b + c;

declares an integer variable that will hold some data named 'a' as the sum of other named variables 'b' and 'c'. Notice that there is no longer a one-to-one correspondence between the machine language and the high-level language. The machine language may not even have a concept of declaring a variable to be an integer. But, every machine has a different machine language. If I can write a compiler that will translate the high-level language into a particular machine language, though, I can even use the same high-level code on different incompatible machines! This portability comes with the caveat that I myself not use any operations in my program that aren't supported by all machines. That way, as long as I have a compiler that knows how to translate the code, it will work anywhere.

Until this point, the programmer had to know the details of how every component in the computer worked before he could control it. Now, with high level languages and high level libraries, the inner-workings are abstracted and generalized. Details are hidden, but new tools are available for the bigger picture of what the programmer wants the machine to do.

The high-level language is not a human language. Any high-level language construct must be entirely deterministic with no ambiguity. A dumb computer still doesn't understand anything about the context of the strings of numbers that represent the high-level language. And, in order to run it, it must end up in machine language.

I want you to step back for a moment from the details and notice what has been done. At each step, the programmer-computer interface has been abstracted. The nitty-gritty details of how the functionality is achieved have been lost, and entirely new (and useful) constructs have been generated. In the end, all of them get translated into the only language the computer understands -- machine language. Implicitly, by choosing a high level language and high level libraries, the programmer has traded off the detailed control of every operation that computer performs for the ability to spend time not dealing with those low-level details. Next time, I will actually discuss a user interface (really!).

Technorati tags: ,

Comments:
You know you're in trouble when I can keep up with a tech post. J/K looking forward to the next installment.
 
Cool! Someone reads these! Actually, the intended audience of this series is non-techie people. Thanks for commenting :-)

I'd suggest catching up on the other 2 (the first one is basically a long joke) if you want to catch my major drift.
 
Post a Comment

<< Home