wiki.paulswartz.net

Navigation

Recent site activity

Home‎ > ‎DivisionIII‎ > ‎

A Tower of Languages

We have no way to live without language; our brains are programmed to learn language, and we start as soon as we are born. However, we rarely choose the language we learn originally. It is our mother tongue, the language spoken at home as we grow up. We can learn other languages later in life, but we will generally be the most fluent in our mother language because it is the language we have the most experience with.

This simple fact of natural languages is not true of the artificial languages of the computer. Paul Graham makes exactly this distinction: "If you write in Latin, no one can understand you. But Lisp is a computer language, and computers speak whatever language you, the programmer, tell them to."[1] As a dead language (no one speaks it as a first language), Latin is not very useful because there are so few speakers. But Lisp, a language that very few programmers use due its strange syntax, is still just as functional as it was in the 1960s when it was created. Every programming language is understood equally well by the computer, but programmers use a wide variety of languages. Why then do programmers make different language choices?

Perhaps it is best to start with the different languages the computer can understand. The computer works by pushing small electrical charges around a maze of circuits. The circuits in the central processing unit (CPU) follow instructions based on instructions in binary code; the circuits in the memory store information for later use by the CPU. This binary code is the basic language of the computer, but no one except processor designers actually works at this level. It is too low-level; the language is so rudimentary that solving complex problems becomes too difficult.

One step up from binary code is assembly code. This level gives human-readable names like MOV and ADD to the binary codes the CPU understands. A program called an assembler turns this into the actual binary code. This language too is generally eschewed except by operating system programmers, who require direct access to the CPU, but do not want to deal with the hassle of remembering all the binary codes for operations.

Next is object code, which is binary instructions, but not for the CPU. Object code is directed at an operating system, like Microsoft Windows or Linux. Operating systems take care of operations like file and memory management, and free programs from having to worry about the specifics of particular CPUs.

Source code is the highest level understood by a computer. Written by humans and meant to be understood by humans (as well as computers), it takes a program like a compiler or interpreter to make the source code a program the user can interact with. Most programmers interact with the computer at this level.

At each of these levels is a different language, and a programmer can choose to write her code at any of these levels. However, the different languages force the programmer to structure her code in different ways. Simple languages, like assembly, force the programmer to think simply. By making her think about many small details, the language makes having larger thoughts more difficult. Higher-level languages, like C++ or Java, take care of tasks that most programs perform (like memory management, access to input/output facilities) and give higher-level abstractions like functions and arrays. This allows the programmer to concentrate more on the problem and less on the specific way the solution must be expressed. No one language is the natural language of the computer; the computer does not speak any higher-level language better than any other. This leaves the choice up to the programmer.

Not only does the computer not care which language is chosen, no language is fundamentally more powerful than any other. This insight stems from work done by Alan Turing and Alonzo Church on the calculability of mathematical functions. The result is the Church-Turing thesis that states that any function that can be effectively calculated can be done so by a computer with a language simpler than binary code. What this means is that no language is more powerful than any other in pure mathematical terms.

However, in real-world terms, some languages are more powerful than others. Higher-level languages are called that for a reason. They make the simple tasks that programmers have to do easier, and the harder tasks possible. Commands that would take pages of lower-level code can be done in just a few lines of higher-level code. A simple command like 'a = b + 1' in a higher-level language translates to this in the much larger assembly language:

load register1, [memory location of b]
store register2, 1
add register1, register2
save register1, [memory location of a]

Since the computer understands all of the languages, the programming language choice can be based on factors other than mere mathematical power. Programmers take into account other considerations: simplicity of the language, efficiency and clarity of the code, testability of the application, libraries, technical support, etc. But most programmers have a favorite language, a language that they choose to use when they can. The choice is important to the programmers because they start to think in their favorite languages: "Whatever language people happen to be used to, they tend to consider just good enough."[2] They see solutions to problems in the terms of the language they are used to.

Natural languages have the same effect on speakers. After puberty, the languages children speak have patterned their brains. Learning a second language requires the brain to store the new language information in a different place.[3] Speakers also start to adopt language idioms, phrases which have language-specific meaning: 'kicking the bucket' in English means 'dying.' Computer programmers have idioms as well, phrases that they use in specific languages to solve frequent tasks. In Python (an interpreted programming language), an idiom for a do/while loop (a loop that exits at the end instead of the beginning) is:

while True:
[do something]
if [exit condition]:
break

This pattern appears frequently in Python code to replace a structure that is built into other languages.

A more general pattern that occurs in many computer languages is called a 'design pattern.' These patterns solve general design problems (generally in object-oriented languages) that happen over and over again. An example of a design pattern is the Singleton object. A Singleton is an object of which only a single copy exists. No matter how many times the Singleton is created, the object is always the same. The use for this kind of object is generally to save state (a unique configuration of information) or to coordinate actions throughout a system. A Singleton keeps all of the relevant data in a single object. Instead of the programmer having to load the data each time, he just instantiates (to create an instance of an object) the Singleton object. The data will be there, and be the same, each time the object is instantiated. Each language has a different way of making a Singleton object, depending on the features that are available in the language and the prevailing idioms.

But not all high-level languages are created equal. Graham argues that there is a hierarchy of programming languages, with machine language and early languages like Cobol at the bottom, and higher-level languages like Java or Lisp closer to the top. From the middle of the hierarchy, it is easy to see down; languages lower down do not have features that are present in the higher-level languages (Graham uses the example of recursion). But looking up the hierarchy is more difficult. A language higher up the hierarchy doesn't immediately seem more powerful: when a programmer looks up, [w]hat he sees are merely weird languages. He probably considers them about equivalent in power to Blub [a hypothetical higher-level language], but with all this other hairy stuff thrown in as well. Blub is good enough for him, because he thinks in Blub.[4] However, despite the seeming equality, languages higher up the hierarchy allow entirely different, much easier, solutions to problems. Going back to Graham's example of recursion, a language which allows recursion allows for an entire class of solutions that are not possible in non-recursive languages.

This kind of hierarchy is interesting because it is not present in natural languages. It is generally assumed that no natural language is better than any other, possibly because of the controversy such a hierarchy would create. It would be difficult to create a natural language hierarchy because there are no features of natural languages that allow for the creation of a hierarchy.

Features of programming languages that would allow ordering include: functions, recursion, objects, and macros (programs that operate on programs). Each of those features makes a kind of algorithm or structure easier. The more of those features a language implements, the easier programs are in the language, and the higher the language sits in the hierarchy of programming languages. Graham doesn't discuss ordering languages in the middle of the hierarchy, but does pick what he thinks is the most powerful language: Lisp. He picks Lisp because it is essentially a meta-language, a language that can operate on itself. Lisp code is a data structure that can be operated on with macros, "programs that write programs."[5] Lisp code can operate on Lisp code.

This ability to operate on itself is one of the qualities that make natural languages so powerful. I can talk about the English language using the grammar and the vocabulary of the English language; I don't need to depend on another language. However, there are other features not replicated in programming languages that make the natural languages more powerful. The difference most obvious to anyone who has learned to program is natural languages' tolerance of ambiguity.

When we read a shampoo bottle in the shower, we know that the instructions to 'Lather, Rinse, Repeat.' are conditional: we only repeat washing our hair while it is dirty. We do this because we understand implicitly that the loop should only continue while it needs to. Computers do not understand commands the same way we do. Computers only do exactly as they are told, for as long as they are told to do so. The shampoo loop is called an infinite loop in computer science for the obvious reason: the computer freezes, stuck in the loop for an infinite amount of time. The only way to get the computer out of the loop is with force external to the program (a hard reset).

Natural languages are extraordinarily capable of describing and interacting with the world. They must be tolerant of ambiguity because so much of the human experience is ambiguous. To handle the vast amount of data that comes from processing the world, humans resort to metaphor. We tend to think of metaphors as being a literary flourish, a simple comparison. But metaphors are an integral part of natural languages, one that structures our interaction with the world.

George Lakoff wrote the first book to seriously look at the use of metaphor as a structure of thought. In it, he examines the ways in which we use metaphors in our everyday life. According to Lakoff, "the essence of metaphor is understanding and experiencing one kind of thing in terms of another."[6] Our experience is so rich that, to make sense of it, we have to relate concepts to one another. Creating new language for each individual concept would be too difficult. Metaphor allows us to reuse language and save space in our heads.

Metaphors are a consistent system based in physical and cultural experience. The system is consistent because the metaphors within are not contradictory: we can not have HAPPY is UP as one metaphor and SAD is UP as a different metaphor. Instead, all emotions that are up ('my spirits rose') are happy, and those that are down ('down in the dumps') are sad. The metaphors we use are derived from our physical and cultural experience: we derive metaphors from our experience in the world. HAPPY is UP in part because people who are happy are likely to be up and moving around, whereas sad people can likely be found sitting or lying down.

Metaphors highlight some elements of the comparison of the objects involved, and hide others. For example, the metaphor ARGUMENT is WAR hides the conversational aspects of an argument in favor of their nature as a conflict. No metaphor is a perfect comparison, as no two concepts are identical. Not all parts of a metaphor are commonly used either; they "play no particularly interesting role in our conceptual system, and hence are not metaphors that we live by."[7] While we may talk about the foot of a mountain, we rarely if ever discuss the ankle or knee of a mountain. The other parts of the MOUNTAIN is BODY metaphor go unused.

The computer itself is one of the most powerful metaphors we have. With software, we can see this abstract machine in terms of whatever device we want. While I'm writing this essay, the computer is using the terms of a typewriter. The default keyboard layout is a relic of this metaphor: when typewriters were first created, they were not good enough to keep up with the typists using them and so the keys were laid out to make typing slower (the QWERTY layout). Computers today still use this layout, despite the fact that the computer can keep up with the fastest typists. When I manage the documents on my hard drive, the computer uses the terms of a file cabinet: files go into folders, which can be organized and sorted. The computer can act as so many different things because ultimately it is the software that is in control of the computer, is the computer for the user. Almost no one cares what's under the hood as long as it runs the right software.

We have also brought the computer into our linguistic metaphors. Metaphors like COMPUTER as SERVANT (computers do what we tell them to do), COMPUTER as WORKPLACE (and more generally COMPUTER as PLACE), and BRAIN as COMPUTER have worked their way into our consciousness.

"New things are hard to talk about,"[8] and the way that we start to talk about them is by creating metaphors that use the new things. Computers are one of the newest creations, and so we have created new metaphors in order to better interact with them. The field of artificial intelligence is based on the metaphor that the human brain could be simulated by a suitably programmed computer. We talk of the Internet as if it were a physical place like Las Vegas, when it is only a set of computers speaking a common language. These metaphors dictate how we interact with computers. We treat our computers as our servants, instead of as equal participants in the work we do. Despite the fact that computers help us find, process, and disseminate information, we still treat them as workers because that is how our metaphors structure the relationship.

Design patterns are but one expression of metaphor in computer languages. Like natural languages, computer languages also use a metaphorical structuring to save space and make using the language easier. Metaphor in computer languages functions as a kind of abstraction, a displacement of detail into a different level. As with the design patterns, once they are created the programmer does not have to think about how they are implemented. This displacement, like natural language metaphors, highlights some aspects of the lower level of abstraction and hides others.

Each of the levels of computer language above can be seen as a type of metaphor. Each level allows access to the level below, but in different and easier-to-manage terms. Very few programs actually care about how the CPU functions – they do not need to care because knowing exactly how the CPU does an operation is rarely relevant as long as the CPU does the operation correctly. But because there is an abstraction, in the form of an operating system, the programmer can work with the system in terms of higher-level functions. These levels structure our interactions with the computer, both as users and as programmers, just as metaphors structure our interactions with the world and other people.

There is another level of human-computer interaction besides the code, and that is the user interface. The user interface level is where the user cares most about the metaphors in the software. Programs that use consistent metaphors are easier for users to understand. Consistency here can be consistency within a program, by using related terms for related functions. Microsoft Windows is famous for disregarding this by putting the shutdown functionality in the Start menu. Or this can be consistency with other programs. Nearly all office programs have the same menu structure: File for saving, opening, and printing; Edit for undo/redo and copy/paste; Insert for adding things to the document; Help for assistance. Or it can be consistency with the real world.

The metaphors we live by are successful because they link things that are different but that we interact with or experience in a similar way. The applications on the computer are different from the objects we interact with in the world; they are virtual objects. But if those applications use the same terms for virtual objects as we use for real objects, the applications are easier to use. The essence of metaphor is seeing a concept in the terms of another concept; the essence of a virtual object is seeing that object in the terms of a physical object.

Graham discusses a style of programming in which programmers create a new, system-specific language for a project, and then write their program in the new language. He calls this style "bottom-up programming" because the programmers build the language up from the bottom to the application. The programmer builds new metaphors into the language, and then writes the program to work with those metaphors. It is contrasted with top-down programming, which is much more structured; it starts at the highest level of abstraction and is programmed down towards the language.

For Graham, bottom-up of programming has several advantages:

  1. It makes programs shorter – by building up the language, the actual application can be much shorter, even including the new language code.
  2. Reusing code is easier – by starting with abstractions at the bottom, writing duplicate code is avoided. Top-down programming typically has this problem. Separate modules of a top-down program may perform related operations, but because they are separate modules they will not share code; those operations will be duplicated.
  3. Understanding the program is easier – ideally, the new abstractions are like natural language metaphors; they function behind the scenes, and speakers/programmers do not think about them. Abstractions that have just been refactored into a new function generally require reading their code for their meaning to be understood.
  4. Programs are easier to write – programmers can think at a higher level, and have to write less code to accomplish their goals.[9]

All programming languages include metaphors that build up the language. Some even provide built-in data structures like lists or trees (a structure where parent nodes have child nodes, and each child can be a parent to other nodes, etc.), or implementations of common algorithms. The more the language is built up, the higher-level the program can be. Bottom-up programming is the extension of this to the actual program; the program continues the language buildup that has always been started.

One of the most interesting metaphors to come into use around computers is also one of the most relevant to this work: COMPUTING is a LINGUISTIC ACTIVITY. Computing, both using programs and creating them, is a linguistic activity like speaking or writing.[10] Like other linguistic activities, interacting with the computer has its own set of languages. Computer languages, like natural languages, structure the minds of the people who use them. Therefore, it's important to think about what kind of structuring is happening.

Programmers start to pick up idioms in their programming languages, and solve problems most efficiently in languages they are more comfortable with. Metaphors are also a productive area of comparison. Programmers create new metaphors when they write programs, and depend on earlier metaphors as a way to think at a higher level. Like natural language metaphors, programming language metaphors help to structure the works that use those metaphors.

However, computer languages also show their roots as the interface to a machine, and function differently from natural languages in key ways. We never have a choice about our first natural language; we learn up the language we hear most frequently as we develop. Computer languages are chosen by programmers to perform tasks, and unlike speakers of natural languages, the computer does not care what language that task is written in. The most popular natural languages today all have roughly the same features, and the same information can be conveyed accurately in any of the languages. The most popular programming languages, however, differ significantly in the features they provide to the programmers who use them. Programming is indeed a linguistic activity, but one where the choice of language is much more important than in our natural, human languages. A programming language can be the difference between getting a program done early, and getting the program done at all.

Notes

  1. Graham, Paul. “Beating the Averages.” (2003). 13 April 2007 <http://www.paulgraham.com/avg.html>.
  2. Ibid.
  3. Dehaene, Stanislas. “Fitting two languages into one brain.” Brain. 122.12 (1999): 2207-2208. <http://brain.oxfordjournals.org>.
  4. Graham, Paul. Ibid.
  5. Ibid.
  6. Lakoff, George, and Mark Johnson. Metaphors We Live By. University Of Chicago Press, 1980.p. 4.
  7. Ibid. p. 55.
  8. Lawler, John M. “Metaphors We Compute By.” (1987). 13 April 2007 <http://www-personal.umich.edu/~jlawler/meta4compute.html>.
  9. Graham, Paul. “Programming Bottom-Up.” (1993). 13 April 2007 <http://www.paulgraham.com/progbot.html>. Also, see "White Boys' Code" in this collection for a discussion of bottom-up programming and its relationship to gender.
  10. Lawler, John M. Ibid.