Programming Language
Put two humans together, and you get language. Put a human and a computer together, and you get a new programming language. We can't help but invent symbols and combine them in formulaic ways in order to communicate. Even if an existing language is given to us, we modify it with shortcuts and new structures. We are so quick to shape our languages that some cognitive scientists believe language is instinctual, rooted not just in culture but our very biology.
Nevertheless, we live in the days of graphical applications and LLMs, and we may rightly wonder if our language instinct is obsolete. Is learning about or inventing new languages—human or programming—a good use of our time? Why write code if we can just click a button or drag a slider to make the computer do things? Several programming language researchers argue that GUIs have less expressive reach:
While both GUIs and languages are often designed around making it easy for users to say common things, a language empowers users to say uncommon things too.
Okay, but why write code if an LLM can do it for us? Isn't the only programming language we need natural language? The logic behind this claim is often seated in an historic analogy. People used to write in assembly. Then compilers came along. Initially people said they could write better assembly than a compiler and refused to use them. Compilers got good, and almost no one writes assembly now.
This feels like the same structure as we're going through right now, with LLMs threatening today's programming languages. However, there's a difference. When we wrote assembly, we were bending ourselves to the machine. That's because assembly is closer to plugging in wires than describing a computation. When the machine changed, so did the assembly. Who wants to write in such a language so brittle and removed from human thinking? Not us, so we built better languages that were closer to how we wanted to think.
Now we have LLMs that interpret natural language. The machines are bending to us. The issue with this is that we ourselves are bendy. Natural language is vague and ambiguous. “Move that there.” What is that? Where is there? Once a student wrote this about me on Rate My Professors: “my favorite professor to date.” We resolve the deficiencies of natural language by piling on more words to expand and clarify. Humans pile on with conversations, and LLMs pile on with training data, verbose specifications, and corrective prompting. With natural language, we must choose between brevity and clarity.
Both assembly and natural language are two undesirable extremes. Let's pick something in the middle: a language meant for describing computation that has a formal grammar and affords precision with minimal concern for the quirks of the host machine.
People who publish books are called writers, but really their job is to think. The tasks of writing and thinking are hard to separate. If writers think but don't write, their thoughts are lost. They usually don't know what they think until they start writing. So it is with us. We call ourselves programmers, but really our job is to build technology. We don't get technology without code, and we come to understand technology by programming. If we want new and better technology, we should not abandon programming languages.
In this chapter, we'll continue mulling over these questions as we examine programming languages as a subdiscipline of computer science. By the end of this chapter, you'll have first-draft answers to these questions:
- Why are there so many programming languages and why do we study them?
- How does a program written in a high-level language get turned into a form that the CPU can execute?
- How do we describe the syntax of a new language so that others can understand it?
- What algorithms will take us from source code to an abstract syntax tree?
In the last chapter, we started looking at Ruby. We'll keep using Ruby in this chapter, but our focus is on building a brand new programming language. We will decide what it looks like and what it does. And we'll build the tools that translate programs written in this unknown language into a known language that the CPU can execute.