Tuesday, April 7, 2015
Listening to your customers
Many companies follow this wisdom. Microsoft has done so. One of the biggest messages that customers tell Microsoft is that they want their existing systems to keep working. They want their e-mail and document systems (Outlook and Word) to keep working. They want their databases to keep working. They want their custom-built software to keep working. And Microsoft complies, going to great lengths to keep new versions of operating systems and applications compatible with previous versions.
Apple, in contrast, does not keep their systems compatible. Apple constantly revises the design of their hardware and software, breaking backwards compatibility. The latest Macbooks use a single connector (a USB type C connector) and drop all of the older connectors (network, power, USB type A, Thunderbolt, etc.). Apple has revised software to drop old features. (Mac OS X "Lion" removed support for 32-bit processors.) Is Apple not listening to its customers?
I believe Apple *is* listening. Apple gets a different message from customers: They want their systems to "just work" and they want their systems to be "cool". Those elements mean that the system design must change (especially to be "cool"). Apple offers new, cool systems; Microsoft offers stability.
Monday, April 6, 2015
Web services use the Unix way
Web services use a very different mindset than the typical Windows application. Windows applications are large, all-encompassing systems that contain everything they need.
Web services, in contrast, are small fractions of a system. A complete system can be composed of web services, but a web service is not a complete system.
We've seen this pattern before. Not in Windows, nor in PC-DOS or MS-DOS, but in Unix (and now Linux).
"The Art of Unix Programming" by E.S. Raymond covers the topic well. The author describes the philosophy of small, connectable programs. Each program does one thing well, and systems are built from these programs. Anyone who has worked with the Unix (or Linux) command line knows how to connect multiple programs into a larger system.
We don't need to discover the properties of good web services. We don't need to re-invent the techniques for desiging good web services. The properties and techniques already exist, in the command-line philosophy of Unix.
Thursday, April 2, 2015
Mobile operating systems break the illusion of control
The dawn of computers saw the hardware emerge from electro-mechanical relays, but no software. The first computers were programmed with wires connecting components to other components. A computer was a custom-purpose device, designed and built to perform one calculation. Shortly after, the "programming wires" were isolated to removable boards, which allowed a "program" to be removed from the computer and stored for later use.
The first programs (in the sense we know them) were sequences of numerical values that could be loaded into a computers memory. They were a softer variant of the wired plug-board in the earlier computers. Building the sequence of numerical values was tedious; one had to understand not only the problem to be solved but also the processor instruction set. These sequences are now called "machine language".
Programmers, being what they are, developed programs to ease the chore of creating the sequences of machine language values. These programs were the first assemblers; they converted symbols into executable sequences. A programmer could work with the much easier to understand symbolic code and convert the symbols to a program when his changes were done.
Up to this point, the operation of the computer has been a simple one. Create a program, insert it into the computer, and let it run. The program instructed the computer, and the computer performed the calculations.
There were no operating systems. It was the computer and the program, alone together in the small universe of computing hardware. The program was in charge and the computer obeyed its instructions. (Blindly and literally, which meant that the programmer had to be precise and complete in his description of the operations. That aspect of programming remains with us today.)
The first operating systems were little more than loaders for programs. Programmers found that the task of loading an executable program was a chore, and programmers, being what they are, creating programs to ease that task. A loader could start with a collection of programs (usually stored in a deck of punch cards), load the first one, let it run, and then load and run the next program.
Of course, the loader was still in memory, and the loaded programs could not use that memory or overwrite the loader. If they did, the loader would be unable to continue. I imagine that the very earliest arrangement worked by an agreement: the loader would use a block of addresses and the loaded programs would use other memory but not the block dedicated to the loader. The running program was still "in charge" of the computer, but it had to honor the "loader agreement".
This notion of "being in charge" is important. It is a notion that has been maintained by operating systems -- up to mobile operating systems. More on that later.
Operating systems grew out of the concept of the loader. They became more powerful, allowing more sharing of the expensive computer. The assumed the following functions:
- Allocation and protection of memory (you can use only what you are assigned)
- Control of physical devices (you must request operations through the operating system)
- Allocation of CPU (time slices)
These are the typical functions we associate with operating systems.
Over the years, we have extended operating systems and continued to use them. Yet in all of that time, from IBM's System/360 to DEC's VMS to Microsoft's Windows, the understanding is that our program (our application), once loaded, is "in control" until it exits. This is an illusion, as our application can do very little on its own. It must request all resources from the operating system, including memory. It must request all actions through the operating system, including the operating on devices (display a window on a screen, send text to a printer, save a file to disk).
This illusion persists, I believe due to the education of programmers (and system operators, and computer designers). Not merely through formal training but also through informal channels. Our phrases indicate this: "Microsoft Word runs and prints a document" or "Visual Studio builds the executable" or "IIS serves the HTML document". Our conversations reinforce the belief that the running computer is in control.
And here is where the mobile operating systems come in. Android and iOS have very clear roles for application programs, and those roles are subservient to the operating system. A program does not run in the usual sense, making requests of the operating system. Instead, the app (and I will use the term "app" to indicate that the program is different from an application) is activated by the operating system when needed. That activation sees the operating system instruct the app to perform a task and then return control to the operating system.
Mobile operating systems turn the paradigm of the "program in control" inside-out. Instead of the program making requests of the operating system, the operating system makes requests of the program. The operating system is in control, not the program.
This view is very different from our traditional view, yet it is an accurate one. Apps are not in control. Applications are not in control -- and have not been for many years.
Tuesday, March 31, 2015
Our tools shape our languages, and our programs
When designing applications, languages are sometimes overlooked. Yet it should be obvious to anyone who has used multiple languages. Different languages have different capabilities. C and its descendants have the notions of pointers, something absent in COBOL, Fortran, and BASIC. The notion of pointers lets one build dynamic, complex data structures; such structures are not possible in pointerless languages.
Our tools have an effect on our languages, which can have an effect on our applications.
In the Microsoft world, C# is a simple language, yet the .NET framework of classes is complex and wordy. A simple application in C# requires a significant amount of typing, and a moderately complex application requires a large amount of typing.
Languages such as Perl, Python, and Ruby require less typing. How is it that the .NET framework is so complex? The answer, I believe is in Visual Studio.
Microsoft's development environment is the result of competition and years (decades?) of development. Over that time, Microsoft has added features: debugging, test case management, design documents, and editing features. One significant editing feature is the auto-completion of typing with context-sensitive information. When I am typing a C# program and I start typing a name of a variable, Visual Studio gives me a list of existing variable names. (It also knows that the declaration of a variable is different from the use of a variable, and does not prompt me with existing names.) Visual Studio also lists methods for objects and provides templates for parameters.
This assistance from Visual Studio reduces the burden of typing. Thus, applications that use the .NET framework can appear wordy, but the effort to create them is less than it appears. (Some have argued that the assistance from Visual Studio helps reduce errors, as it suggests only valid possibilities.)
Thus, a development tool can affect our languages (and class frameworks), which in turn can affect our applications. When choosing our tools, we should be aware of their properties and how they can affect our projects.
Tuesday, March 24, 2015
Why Alan Turing is a hero to programmers
Yet we do have a few heroes, a few programmers who have risen above the crowd. Here is a short list:
Grace Hopper Serving in the Navy, she created and advocated the idea of a compiler. At the time, computers were programmed either by wire (physical wires attached to plug-boards) or with assembly language. A compiler, converting English-like statements into machine instructions, was a bold step.
Donald Knuth His multi-volume work The Art of Computer Programming is a comprehensive work, comprising machine design, assemblers, compilers, searching, sorting, and the limits of digital computation. He also created the TeX language and the notion of "Literate Programming".
Brian Kernighan and Dennis Ritchie Created the C language.
Larry Wall Creator of the Perl language; also the creator of the 'patch' program used to apply changes across systems.
Fred Brooks The chief designer of IBM's operating system for the System/360 computers, his book The Mythical Man-Month has several interesting observations on the teams that construct software.
Gerald Weinberg Known for his books on system analysis and design, I find his work The Psychology of Computer Programming much more useful to programmers and program team managers.
All of these folks are (were) smart, creative, and contributors to the programming art. Yet one has a special place in this list: Alan Turing.
Alan Turing, subject of the recent movie "The Imitation Game", has made significant contributions to the programming craft. They are:
- Code-breaking in World War II with the Bombe computer
- The Turing Test
- Turing Machines
- The Halting Problem
All of these are impressive. Turing was many things: mathematician, biologist, philosopher, logician.
Of all of his accomplishments, I consider his proof of the halting problem to be the one act that raises him above our other heroes. His work with code-breaking clearly makes him a programmer. His idea of the Turing Test set clear (if perhaps unreachable) goals for artificial intelligence.
The notion of Turing machines, with the corresponding notion that one Turing machine can emulate another Turing machine is a brilliant insight, and enough to make him a hero above others.
Yet it is the halting problem, or more specifically Turing's proof of the halting problem (he proved that one cannot tell, in advance, that a program of sufficient complexity would be guaranteed to stop) is what pushes him across the line. The proof of the halting problem connects programming to mathematics.
Mathematics, of course, has been with us for centuries. It is as old as counting, and has rigorous and durable proofs. Euclid's work is two millennia old, yet still used today. It is these proofs that make mathematics special - math is the "Queen of the Sciences" and used by all other branches of knowledge.
Mathematics is not without problems. There is a proof called the Incompleteness Theorem, which states that any system of axioms (rules) that includes integers, there exist theorems which are known to be true yet cannot be proved with that system of axioms. (The Incompleteness Theorem also states that should you add an axiom to the system to allow such a proof, you will find that there are other theorems which are known to be true but not provable in the new system.)
That sounds a lot like the halting problem.
But the Incompleteness Theorem is the result of thousands of years of study, and computing is young; we have had digital computing for less than a century. Perhaps we can find another corresponding mathematical surprise, one that occurred earlier in the history of mathematics.
Perhaps Turing's proof of the halting problem is closer to the discovery of irrational numbers. The Greek philosophers were enchanted with mathematics, bot geometry and arithmetic. The reduction of physical phenomena to numbers was a specialty for them, and they loved integers and ratios. They were quite surprised (and by some accounts, horrified) to learn that numbers such as the square root of two could not be represented by an integer or a ratio of integers.
That kind of problem sounds close to our halting problem. For the Greeks, irrational numbers broke their nice, neat world of integers. For us, the halting problem breaks the nice, neat world of predictable programs. (To be clear, most of our programs do run "to completion" and halt. The theory states that we cannot know in advance that they will halt. We simply run them and find out.)
Turing gave us the proof of the halting problem. In doing so, he connected programming to mathematics, and (so I argue) us early programmers to the early mathematicians.
And that is why I consider Alan Turing a hero above others.
Thursday, March 19, 2015
The oldest high-level language
The explanation I received at the time was that "low-level languages deal with machine-specific issues and high-level languages do not". I was taught this division when I started programming, many moons ago. It works, but it is awkward. It leads to the following:
Low-level languages:
- Assembly
- Machine language
- C
- C++
- Lisp
- Fortran
- Cobol
- BASIC
- Pascal
- C#
- Java
- Awk
- Perl
- Python
- Ruby
- Eiffel
- Erlang
- Haskell
If every language ends up "winning" in the high-level category, then the rule has little value. So I started thinking about different ways to divide languages.
The old rule is valid: languages which force the programmer to consider machine-specific details are low-level languages. That concept remains. What we need is a refinement, some concept that can split off some of the high-level languages and push them into the "mid-level" category.
For me, that refinement is memory layout.
If a programming language forces the programmer to think about memory layout, then it is, at best, a mid-level language.
This refinement is consistent with our original rule of machine-dependency. Machine language and assembly language are low-level languages (by convention) and they require attention for the layout of items in memory. Indeed, machine-language and assembly language require the programmer to think about the layout of instructions as well as data.
C and C++, with their pointers, require a fair amount of attention to memory layout. They also have structs which aggregate data elements in memory, and a fair amount of control over the alignment of elements within a struct. They stay within the mid-level group.
Cobol, normally considered a high-level language, drops to mid-level. Anyone who has programmed in Cobol knows that much attention is given to the layout of not only input and output records but also working storage (what most programmers would call "variables").
Fortran also drops to mid-level. Its handling of arrays, its COMMON blocks, and its EQUIVALENCE statement all make the programmer think about memory allocation.
Pascal too moves to mid-level, due to its pointer concept.
With this refinement, we now have the following list:
Low-level languages:
- Assembly
- Machine language
- C
- C++
- Lisp
- Fortran
- Cobol
- Pascal
- BASIC
- C#
- Java
- Awk
- Perl
- Python
- Ruby
- Eiffel
- Erlang
- Haskell
But I do notice that, of the high-level languages, BASIC is the oldest - and by a fair amount. BASIC was designed in the 1960s. The next languages were Awk (1970s) and Perl (1987). A programmer using BASIC does not have to worry about memory layout; variables are instantiated and reclaimed transparently. The BASIC language is limited, although it has been extended over time.
So using my refined heuristic, the oldest high-level programming language is BASIC.
Thursday, March 12, 2015
Smart watches are watches after all
Apple made a big splash in the news this week, finally announcing their smart watch (among other things). Apple calls their product a "watch". Is it? it seems much more than a plain old watch.
Apple did the same with their iPhone. The iPhone was really a small computer equipped with wireless communication. Yet who wants to carry around a small computer (wireless or not)? It's much easier (psychologically) to carry around a phone.
Looking at our experience with the iPhone, we can see that iPhones (and smart phones in general) are used for many purposes and only occasionally as a phone. We use them to take pictures, to watch movies, to play games, to read e-mail, to send and receive text messages, to navigate, to calculate, to bank online, ... The list is extensive.
Applying that logic to the Apple Watch (and smart watches in general) we can expect many purposes for them. Some of these will duplicate or extend our phones (those small wireless computers): notifications of appointments, navigation, display of e-mail and text messages, and of course to tell the time. Smart watches will offer new functions too: payments at checkout counters, unlocking house doors (equipped with smart locks), unlocking automobiles (and possibly replacing the key entirely), exchanging contact information (virtual business cards), ... the list is extensive.
Smart watches will provide convenience. Smart watches will also add a degree of complexity to our lives. Is my watch up to date? Have I charged it? Is my watch compatible with a merchant's payment system? Does it have a virus or other malware?
We'll call them "watches", since the name "small computing device that I wear on my wrist" is unwieldy. But that was the same issue with the original pocketwatches and wristwatches. They, too, were small computing devices. (Some, in addition to telling time, displayed the phase of the moon and other astronomic data. In the twentieth century, wristwatches often displayed the day of the month.)
So, yes, smart watches are not watches. They do much more than tell time. And yet they are watches, because we define the term "watch" to mean "small computing device that I wear on my wrist". We have for more than a century.