Evolution of Programming Part Three

In the last installment we discussed several of the popular paradigms of programming languages. We talked about Structured Programming, Object Oriented Programming, and Functional Programming. In this installment we are going to look at programs from a different perspective.

Early computers were operated as free standing machines. They could receive input from tape drives, disk drives, or keyboards. They could send output to printers, tape drives, disk drives, or video displays. They could send date to other computers over serial lines but the transfers were typically manually initiated on both the sending and receiving computer.

Then various computer manufacturers started coming up with schemes for connecting multiple computers together and programming to talk among themselves in more autonomous ways. The early networks were restricted such that they only operated between computers made by the same manufacturer running the same operating software.

Then the Defence Department’s R&D branch, DARPA, started funding research to try to build a computer network that would talk between heterogeneous computers and would survive a nuclear attack. The idea was to build a set of network protocols that would detect the most efficient way to route data through the network and would adapt to failures of any given network paths by finding alternative paths.

The researchers that built the internet would hold workshops where they would get together and connect their computers together and attempt to get them to talk to each other. Their was an agreement among them that the first ones to get there machines to talk would by doing so establish the definition of how that particular protocol worked. There was a lot of healthy competition to be the first to get each layer of the network to talk to the others.

I mentioned network layers above and that deserves a little bit of elaboration. Networks were built in layers that went from the lowest level that interfaced directly with the hardware and only transmitted and received data on behalf of the layer above it. Each successive layer added more sophisticated features such as guaranteed delivery of data in the same order that it was sent, and guarantees that the data arrived intact, for example. These layers were available for use by programmers in the form of libraries.

The highest level interface was known as the application layer. One of the first application protocols was the email protocol. It allowed someone on one computer to send email on another computer in much the same manner as we do today.

Another early application protocol was file transfer protocol or FTP. The people that wrote these protocols soon learned that it was easier to debug them if the components of the protocol were comprised of human readable text fields. Thus an email consisted of the now familiar fields such as “TO: username@hostname.domain” and “SUBJECT: some descriptive text”. This was carried over to other protocols.

After the internet protocols were widely established and in use in computer centers around the world, the inevitable thing happened. A researcher at CERN named Tim Berners-Lee was trying to cobble together a system for scientists to share their papers with one another. Thanks to work on computer typesetting software that was readily available at the time, the scientists were used to good looking electronic documents that had various typefaces and embedded graphics, photographs, and even mathematical equations. Tim Berners-Lee came up with a protocol that he called the HyperText Transport Protocol (HTTP) that allowed for the data in the papers to be exchanged along with all the supporting information such as which fonts to use and where to find the images. While he was at it he implemented a language called HyperText Markup Language (HTML) that had facilities for specifying the structure of the document content. One of the more clever components of HTML was the mechanism for making certain elements in the document act as links to other documents such that if you clicked on them in the browser, as the document display program was called, the other document was retrieved and replaced the first document in the browser.

This Hypertext capability was incredibly powerful and caught on like wild fire. In fact, some people would say it was the beginning of another paradigm of programming, the hypertext document. The problem with the original hypertext specification was that it didn’t have any mechanism for the document author to extend HTML.

The browser manufacturers soon remedied that situation. Microsoft embedded their Visual Basic in their Internet Explorer. Netscape came up with a scripting language for their browser. Initially called Mocha, then LiveScript, and finally JavaScript in an attempt to capitalize on the newly found popularity of Sun’s Java programming language. JavaScript never had any similarity to Java other than in it’s name and a cursory similarity in the look of the syntax.

Javascript quickly gained a reputation for being a toy language. In fact it was a very powerful, if slightly buggy, language. It took several years before Google used Javascript to implement Gmail and established that it was a powerful language to be contended with.

The main thing that JavaScript represented was a powerful language that was universally available across all operating systems and all computers. It also had a standard way of producing high quality graphical output by way of HTML and Cascading Style Sheets (CSS). CSS was a technology that was added to HTML to allow the document author to specify how a document was to be displayed orthogonally to  the structure of the document. This comprised a programming platform that ran on all computers and all operating systems without modification. The universal programming language was apparently born.

Sweet dreams, don’t forget to tell the people you love that you love them, and most important of all, be kind.

Evolution of Programming Part Two

In the last installment we had traced programming from an electrical engineering activity involving patch cables through assembly language and FORTRAN and then Lisp. There were many other early computer languages. Some were interpretive like Lisp. Others were compiled like FORTRAN. All of them sought to make it easier and faster to develop working programs. But they all overlooked one fundamental fact.

The purpose of programming languages was for programmers to communicate the details of their algorithms to other programmers. The production of an executable binary (or the interpretation of the source code by an interpreter) was a motivating by product but the same results could have been theoretically produced by typing the numeric instruction codes directly into the computer like the first programmers did.

High level languages allowed programmers to examine their ideas in much the same way that an author of prose reads their manuscript. They facilitated experimentation and they served as a short hand for communicating the details of complex computational processes in terms that the human mind could grapple with.

There were many paradigms of programming throughout the years. One of the first that was identified as such was Structured Programming. Many of the early languages had a very simple syntax for altering the flow of execution in a program. They typically consisted of a statement that evaluated an expression and then based upon the value of the expression caused execution to continue with either the next sequential statement in the program or else branch to another location in the program. This simple construct was how the program made decisions.

The problem was that in those early languages programmers often found themselves in the situation where they wanted the program execution to branch to a different location unconditionally. This was accomplished by a GOTO statement. Both FORTRAN and Lisp had them in one form or another. The GOTO statement made it very difficult to follow the thread of execution of a program. Structured Programming asserted that all programs could be expressed using a small set of control structures, IF, THEN, ELSE, WHILE, UNTIL, and CASE for example. The details of how those instructions work are not as important as the absence of the GOTO from them.

Structured Programming did make programs easier to read but it turns out there were cases when GOTO was absolutely necessary. But it was still considered a construct to be avoided if at all possible. Both FORTRAN and LISP implemented constructs to make it possible to use Structured Programming techniques in them. There were a large number of other languages that supported Structured Programming, notably Pascal and C.

The next popular programming paradigm was Object Oriented (OO) Programming. The idea was that you bundled data, stored in bins called fields, with the pieces of programs that operated on it, called methods. In the first OO language, Smalltalk, the idea was that objects sent messages to other objects. The messages had arguments that made them more specific. The objects would receive these messages, dispatch them to the methods that processed them and return the value that the method computed to the caller.

It turns out that Object Orientation was a very effective means of compartmentalizing abstractions. It made it easier for programmers to visualize their programs in terms of a community of cooperating abstractions.

OOP is still a popular paradigm today. Examples of modern object oriented languages include C++, Java, Ruby, Python, and many others. As it turns out, OOP didn’t replace Structured Programming. Rather, it extended it.

Another popular programming paradigm is functional programming. Surprisingly enough, Lisp was the first functional programming language. One of the key aspects of functional programming languages is the fact that the pieces of programs, called functions or methods, can be manipulated and stored just like any other data. They can be passed as arguments to other functions, and stored in variables to be recalled and executed later.

An example will help to clarify. Suppose that you had a program routine that sorted a list. In many languages that routine would only be able to process a list that only contained all the same kind of data, perhaps all numbers, or all text. It would have to know how to compare any two elements in the list to see what order to put them in. In a functional language you could write a sort routine that would take a comparison function as well as the list of items to sort. Then, if you passed in a list of numbers, you could pass in a comparison function that knew how to compare two numbers. If you passed in a list of text items, you could pass in a comparison function that knew how to compare to text items. The actual sort routine wouldn’t have to know what type of items were stored in the list.

Another aspect of functional languages is the concept of referential transparency. That is a very scary term that simply means that any function that is called with any given set of arguments will always return the same value such that you can replace the call to the function with the value that it returns. This is a very good thing if you have a function that takes a lot of time to compute that gets called multiple times with the same arguments. You can save the result from the first call (called memoizing) and return it any time the function gets called a second time and speed up the performance of the program immensely.

This brings us almost up to how the World Wide Web fits in but it is going to have to wait for part three. Sweet dreams, don’t forget to tell the people you love that you love them, and most important of all, be kind.

Evolution of Programming Part One

At first every computer was designed and built by hand from scratch. The very first computers were programmed by connecting circuits together with patch cables. They were built with vacuum tubes as the transistor had not been invented yet. At that stage programming was primarily an electrical engineering task.

As the state of the art progressed, computers were designed and built by hand but there was an evolutionary resemblance between each successive unit. Some time around this time John Von Neuman came up with the idea of storing programs in the computers memory so that they could be easily modified. Programming, while still a very specialized task, became less hardware engineering and more similar to creating abstract mathematics. Programs were specific to the computers they were written for but the concepts were applicable to other models of computers.

As computer manufacturers started building computers with semiconductors instead of tubes and built many computers with essentially the same design, programmers started sharing small routines to do common tasks like reading from an input device or writing to an output device. These small routines evolved into operating systems.

Up until this time programs were written out on special forms and then converted into punched cards or paper tape with holes in it. These media were then fed to the computer to load the program. The program was run by computer operators who then collected any output generated and returned it along with the input media to the programmer. This was a time consuming process. As anyone who has ever written a program can tell you, the first attempt was rarely exactly as you intended it so there was a lot of head scratching done over memory dumps to try to figure out what went wrong and fix the program so that it could be submitted to run another time.

About this time, someone had a heretical idea. Instead of humans laboriously converting their programs into the numerical codes that the computer processed directly, they would write a program that would allow the programmer to write the program using a symbolic character representation where each symbolic word corresponded to the numeric code of the machine instruction. This representation was called assembly language and it sped up the development of programs by a factor of ten or so.

The next big change to programming was the development of the so called high level language. The first such language was called FORTRAN which was a word coined from the phrase FORmula TRANslation. It allowed engineers to specify programs in terms of the equations that they wanted to solve. This drastically improved productivity again, at least in so far as your program involved computing the solutions to equations.

The next high level language was called Lisp and it was derived from the phrase LISt Processing. Lisp was designed to facilitate the manipulation of abstract symbols. It was based on the idea of a list of symbols. Each symbol was called an atom. These atoms were arranged in lists enclosed in parenthesis. Lists could also contain other lists embedded within them so that when they were written out it seemed to the uninitiated like a lot of arbitrary words with parenthesis sprinkled liberally throughout.

The truth was, Lisp was a revolutionary advance in computing for a number of reasons. First and foremost, Lisp programs were written as lists, just like the data they operated on. This made it easy to write programs that read and wrote other programs. This made it possible for Lisp programs to think about programs in a rudimentary way.  The study of Computer Science exploded in a frenzy of research about the kinds of things that could be represented and computed by a program and the research was largely done in Lisp at the key research centers.

Lisp was also one of the first programming languages that you programmed interactively. Typically the programmer sat at a console and was presented with a prompt, often a greater than character or a dollar sign. They would then type a Lisp expression in. The computer would read what they typed, evaluate it, and print the result. Then it would prompt for another line of input. This process was called  the Read, Eval, Print Loop or REPL for short.

This style of programming encouraged the programmer to explore the problem domain piece meal instead of spending days designing solutions that might not work out when they were finally executed.

A third important attribute of Lisp programs were that they were largely independent of the details of the underlying hardware. Since they were typically stored in the human readable text source form, they could be easily moved from one type of computer to another. There was a bit of up front effort to implement the Lisp language on the new computer but the application programs moved over rather smoothly.

It is interesting to note that as later languages would introduce new features to the programming community it would be found that these features were pioneered by the early computer science researchers using Lisp.

In part two of this article, I will trace the evolution from Lisp to the World Wide Web. In the mean time, sweet dreams, don’t forget to tell the people you love that you love them, and most important of all, be kind.

Diagnosis: Impostor Syndrome

There is a malady that often afflicts creative types. It is called Impostor Syndrome. It is the feeling that one gets when they find themselves being recognized for skills that they are not sure they have. For example, artists early in their career often doubt their bona fides as artists. They have spent their youth in awe of the masters that actually make a living doing the things that they love. When they start to have some success they feel like someone is going to knock on the door and tell them, “Okay. You’ve had your fun. Now it’s time to get a real job.”

Artists aren’t the only ones that are afflicted with Impostor Syndrome though. The software developer works in a field that is constantly changing. New languages and tools are developed so fast that there are few, if any experts in any of them. You see ads on job forums looking for candidates with five years experience in a technology that has only existed for two years at most. Often the only way to get these jobs is to step up and say you know something that you don’t. Then, if you get the job, you hustle like mad to learn the skills that you claim you already have.

Needless to say, this causes a good deal of anxiety among software developers working on the bleeding edge of technology. It is a strange feeling that is unlike most other types of anxiety. Most anxiety is abated when whatever fears that you are anxious about turn out to be unfounded. In the case of Impostor Syndrome, the fears are founded until such time as you demonstrate that they aren’t by actually learning the skills that you have claimed.

When you finally reach the point where you can contribute to a project that you are working on under the shadow of Impostor Syndrome, the relief is palpable. It is an emotional roller coaster ride that takes a kind of adrenalin junkie personality type to enjoy it. The best advice if you find yourself in this position is to take a deep breath and dive in. After all, you were looking for a job when you found this one.

Sweet dreams, don’t forget to tell the people you love that you love them, and most important of all, be kind.

Prescription for a Program

Here is one way to solve a difficult problem. It is described in the context of developing a software solution but the process can be similar for a broad selection of problem domains.

First, ask questions. Ask lots of questions. Ask every question that you can think of. Questions are more important than answers, especially at this stage. Do not be tempted to try to answer these questions at this point. If you look for answers too early, you may stop asking questions before you’ve thought of the important ones.

Write them down as you ask them. You’ll be surprised at how quickly you will forget them if you don’t write them down. Also, if you write them down you can read them later and evaluate them from a fresh perspective. Not only can you read them later, you should look over what you’ve written. See if you have forgotten anything. See if there are any patterns to be discerned among them.

At this point, you can start looking for answers. That doesn’t mean that you shouldn’t capture any good questions that occur to you while you do. Consult with people that are familiar with the problem. In the case of a software project that would include the intended users of the program.

Write a concise description of the problem as you understand it. Review the questions and any answers that you’ve found to see if you have overlooked any details in your problem description.

Next, imagine potential solutions. Write them down as you think of them. Frame them in the form of stories from the perspective of the user of the program. Try to think of several different approaches. Read what you have written and see if any of these stories can be broken down into smaller stories. Keep breaking big stories into collections of smaller stories until you feel like you could write a program that implements one of the small stories.

At some point, pick one of the small stories. You might pick an easy one. That will let you see results quickly and build your confidence. You might pick a  hard story. You may have to struggle more to implement it but you will have a sense of accomplishment when you are done with it. After implementing each story you should write a test framework that demonstrates that it works.

This description has been written as a linear sequence but often in practice it unfolds iteratively. You start out asking questions. You think you are ready to look for answers to them but you think of more questions. The more you learn, the more questions that you have.

As you start imagining solutions your understanding of the problem may be clarified so you can revise the problem description. You may start to implement a story and decide that it should be broken into smaller stories. You may think of more questions at any stage. This is as it should be.

Don’t be afraid to start trying to implement a solution. There is such a thing as analysis paralysis. Software is cheap. The raw material for it is ideas. The principle cost is labor and that is relatively cheap in the broad scope of things. Do experiments along the way to help you understand the problem better. Experiments can also inspire story development.

Finally, understand that you will rarely find a problem that you will be able to completely solve. Usually the best you will be able to do is create a solution that is good enough. It remains for you to decide when you’ve achieved that stage.

This sounds simple but it is hard work. Just remember that you haven’t failed until you quit trying. Sometimes a good night sleep can inspire new perspectives on the problem. Sweet dreams, don’t forget to tell the people you love that you love them, and most important, be kind.

 

Of Puns and Monkey Patching

The English language is plagued by a plethora of words that have many different meanings. Often, words get twisted over time until they mean something entirely different than they originally did. I suspect this is true in most natural languages. It can be the source of many misunderstandings, quarrels, and even wars. It is no surprise then, that computer languages are also subject to these kinds of problems.

One of the first recorded instances was in the original FORTRAN implementation. It allowed the redefinition of literal values. Consequently, you could add a statement like “0 = 100” to the beginning of a program and redefine the value of 0 to be 100. This rendered all of the computation in the rest of the program suspect if not totally wrong.

Being able to redefine the meaning of words in computer languages isn’t always a source of consternation though. In some modern languages, like Ruby for instance, it is used as a mechanism for fixing software. In Ruby, functions are called methods. A method can either be anonymous, in which case it is only useful in an immediate context, for instance as an argument to another method. Or, it can be assigned as the value of a name. Then it can be executed whenever it is required by “calling” the method using its name.

Because of the way that Ruby works, you can also do something colloquially  called “monkey patching”. This works by first assigning the value of a named method to another, auxiliary  name. At this point both the original name and the auxiliary name both refer to the original method. Then, you redefine the original name. You can insert expressions before and after a call to the auxiliary method. This new method is now the new definition for the original method. Any method that called the original method will now get the newly defined method.

To give an example of how you might use this feature, suppose you wanted to print a message “Now entering myMethod” every time you called the method named myMethod. You could easily define the new method to print that message before calling the original method using the auxiliary name. Later, when you were finished using this new monkey patched method to analyze the behavior of the program, you could restore the method back to its original state by assigning the value of the auxiliary method back to the original method.

This is a feature that can easily mess up your program. But it can also let it do things that would require much more effort if done any other way. As Spider-Man’s uncle Ben is known for saying, “With great power comes great responsibility.”

There are other interesting analogies between natural languages and computer languages. We’ll discuss them more in a future blog post.

A Brief Survey of Dynamic Programming Languages

A lot has been written about static languages lately. I am going to focus on dynamic languages. Dynamic languages allow the programmer to develop software by interacting with a “live” language system. The system accepts input and gives feedback about that input immediately as it is entered and evaluated. This encourages an experimental approach to programming. What happens when I do this? Just do it and find out immediately.

I talked at some length about the oldest dynamic language, Lisp, last time. As such, it provides an example of many of the features of subsequent languages. It is classified as a multi-paradigm language. This means that it supports many different styles of programming. You have probably heard of object-oriented programming. Lisp has a very flexible object system called CLOS (Common Lisp Object System). You may have heard of functional programming. Without going into great detail about the details of what functional programming is, Lisp was one of the first languages to treat functions as directly manipulable elements. Lisp also supports rule-based programming, aspect oriented programming, and many other programming paradigms. It has been said that most new languages are just adding features that were pioneered by Lisp.

What other dynamic languages are there? Another venerable and influential dynamic language is Smalltalk. It was developed by researchers at Xerox’s Palo Alto Research Center (PARC). It was the first language to treat every element in the langage as an object. It also was a complete system including editors, code browsers, and a host of other tools, all written in Smalltalk. The system is programmed by interactively defining new objects that are immediately available for use as soon as they are defined. The entire system is graphically oriented. Many of the concepts of modern graphical systems were first introduced by Smalltalk. Unsurprisingly, there are a number of implementations of Smalltalk in use today.

Another dynamic language that has been around for a long time is Forth. Forth is another language that inspires strong opinions. Programmers either love it or hate it. It has been described as write only. It is based on the idea of using a temporary data structure called the stack to temporarily store operands to be used in subsequent computations. The stack is a mechanism where the programmer can “push” values onto the stack and later “pop” the last item pushed. All operations are done on the latest item pushed. This allows an arbitrary number of temporary values to be readily accessible. It also requires the programmer to keep track of exactly what has been pushed onto the stack at any point in the execution of their program. Hence, the “write only” label.

Forth allows the programmer to define new functions interactively through the use of a REPL (Read, Eval, Print, Loop). This is a key feature of dynamic languages and goes a long way toward making up for the cognitive load of keeping track of the contents of the stack.

One of the more recent dynamic language to gain popularity is Ruby. Ruby was inspired by Lisp but adopts the Smalltalk stance that everything is an object. It first gained widespread notice because of a popular web framework written in Ruby called Ruby on Rails. Many of the best practices of modern programming have originated in the Ruby community.

The last dynamic language that I’m going to talk about in this post has had a rather profound impact on computing in the last couple of years. I’m talking of course about Python. Python is an interesting language. Noted for the use of indentation to indicate the start and end of computing blocks and the ease of using code libraries written in other static languages, it has captured the hearts of scientists and educators alike. It is the lingua franca of the popular inexpensive computer the Raspberry Pi. It has carved out a place for itself as probably the most popular modern dynamic language.

In future posts, I’ll talk more about each of these languages and even show some examples of what programs written in them looks like.

Static Vs. Dynamic Computer Languages

I’ve talked about the history of computer languages some in previous blog posts. In particular, I’ve talked about the difference between compiled languages and interpreted languages. Now I’d like to make a further distinction. Some languages are static and others are dynamic. More specifically, static languages are reduced to executable instructions ahead of time during a compilation phase where dynamic languages are processed interactively as the programmer explores the solution space defined by her program.

Static languages can more easily be made to run fast and can more readily be analyzed for correctness. Dynamic languages support organic exploration of the problem and its solution. Dynamic languages can, and have, been made to run fast and analyze themselves for correctness. It’s just typically more difficult to do so.

The surprising thing, to me anyway, is that we have had examples of both kinds of languages pretty much since the first higher level languages were written. FORTRAN was a static language written for engineers to automate the solution of numeric problems. Lisp was a dynamic language to explore the potential for computers to do things other than just numeric computation.

Engineers and Managers liked FORTRAN and its descendants. The applications that were written in them were immediately practical and it was easy to measure how much work the programmers had done. Initially this was measured in Source Lines Of Code or SLOCs. This was all well and good until the programmers realized that a more complicated solution requiring more source lines made them look better to the managers that were measuring their performance.

Over time, we learned a lot about how to write programs in static languages until finally, in recent years, most “serious” programming is done in static languages like C++ and Java. This is a sad situation. Static languages are good for many things but there are a lot of problems that are better served by dynamic languages.

Lisp has a bad reputation among many engineers including many computer scientists. This is often because they were forced to write a project using Lisp in a Computer Languages course by an instructor that didn’t understand Lisp themself. But, to be fair, Lisp does require a little bit of a different mind set from most static languages.

There are also a bunch of myths about Lisp that may have had some basis in fact at one point but over time, Lisp has improved and the hardware that all languages run on have improved, and the myths are, for the most part no longer valid. For instance, it was initially said that Lisp wasn’t any good for doing numerical computing. That was initially true, largely because the authors of Lisp language systems were interested in exploring the non-numerical aspects of programming and consequently made little effort to make the language handle numbers efficiently. This was remedied in the early seventies but Lisp retains a reputation for being bad with numbers in some circles.

The main thing that Lisp is better at than its static conterparts is allowing the programmer to interactively explore a problem space. In a static language, one typically has to write a substantial amount of code before you get to the point where you can run it and see how it behaves. If it doesn’t do what you intend, which is typical for the first couple of attempts, you have to go back to the source and change it, recompile the program, and rerun it to see if you’ve fixed the problem.

With Lisp, one types expressions directly to the interpreter using what is called a Read Eval Print Loop or REPL. The REPL reads a line of code, interprets it and prints the resulting value. Then it repeats the process (hence the Loop). This allows the programmer to explore the problem and potential solutions interactively, getting feedback from the computer after each line. It is amazing how much faster correct programs are developed using this approach.

I’ll talk more about dynamic languages in another post soon. There is much that I want to say about them. And Lisp may have been the first dynamic language but it certainly wasn’t the last. I’ll have more to say about other members of the dynamic language family. I’ll also have something to say about ways that we can realize the best aspects of both of these styles of language by combining them into hybrid languages.

A Different Kind of Programmer

Early on in my career I decided that I liked working with computers that were working correctly better than fixing ones that weren’t. I had already worked as a programmer professionally while I was in the Army and at one other job. I had taken the job as a technician because it was a big raise in salary and had much better benefits. When my new employer found out that I could program, after about six months of working for them as a digital repair technician, they immediately moved me over to programming full time.

I was a rather rare kind of programmer at the time in that I understood computer hardware while at the same time knowing how to program. This landed me in jobs that were known as system programming. System programmers write software that interacts directly with the hardware like operating systems and device drivers. In my case, I wrote network software. This was in the time before the internet when Bill Gates had sworn that computers running Microsoft Operating Systems would never be connected into networks. That was something that big companies did with their big computers. He also said that no one would ever need more than 640 Kilobytes of memory on a PC.

I wrote programs that copied files from one computer to another across the network. This was before TCP/IP became the common software upon which the internet was built. In those days, there were significant differences between the way that files were stored on different operating systems. In particular, DEC computers running the VMS operating system had lots of optional attributes that described files in their system. The Unix file system on the other hand had relatively few. The problem arose when you transferred a file from VMS to Unix. You lost information about the file if it used any of the optional attributes that VMS had and Unix didn’t. Then when you copied it back to the VMS system, it was difficult to restore those attributes so that the programs running under VMS would be able to use the files properly.

We worked for months creating rules that inferred what kind of data was in any given file and what the intelligent defaults were when we created a VMS file from a Unix file. I didn’t realize it at the time, but I was actually doing rudimentary artificial intelligence programming. I enjoyed doing that work a lot.

Years later when I came to work at my current employer, they were using the file transfer software that I had written on their computers. I felt proud of that software but not enough that I wanted them to know I had anything to do with writing it. I was ready to move on to new challenges.

Putting the Horse Before the Cart

Periodically I revisit aspects of programming that excite me. Lately, I have been reading articles by Bret Victor and remembering things that have gotten me excited about programming over time. I have been thinking about how I can incorporate these ideas into my daily work. It is a formidable challenge.

Back during the Elizabethan period there was a revolution in productivity in agriculture. It arose when someone figured out that the ox could pull a plow much easier than it could push a plow. Up until that time they had assumed that an ox would push the plow like a person does.

We have been making similar assumptions about computer programming. In particular, we have structured our computer languages to be easy for computers to translate rather than easy for people to program. This has resulted in programming getting a reputation for being difficult and boring.

Writing a program should be more like creating a painting or writing a song. When I pick up my guitar to write a song, I don’t usually have anything more than a fragment of an idea. Sometimes I may just start playing around with chords and melodies until something strikes my fancy. I write the song by taking these ideas and playing around with them, varying the melody, the harmony, and the rhythm until I discover something interesting.

The very best experiences that I’ve had while programming have been in a similar environment. I have taken an interesting idea and implemented some small aspect of it. Then I try modifying what I’ve written to see what happens when I change different parts of it. It is interesting to observe that in most cases I have been using an interactive programming language. That is, a language that accepts an expression and responds immediately with the result of evaluating that expression.

We learn to build things by trial and error. If we will just change our approach to programming so that it more closely resembles creative play, we will realize an immediate and astronomical increase in productivity. As long as we continue to develop programs like we build bridges producing good software will be an expensive and risky business at best.