Hello • Manahuu • Olá • Hallo • こんにちは • Sawubona • 안녕하세요 • नमस्ते • Bonjour • مرحبًا • Merhaba • Aloha • Cześć • Mabuhay • سلام • Aaniin • Привет • ᎣᏏᏲ • 你好 • Dia duit!
We humans are intelligent, sentient, conscious, embodied beings. We can know, learn, and communicate. We use language, we process information, and we reason. We would like to know what language, information, and reasoning are, and would like to know how we use language, process information, and reason. A powerful way to answer these questions is to not just study these things via observation, but to build machines that can do these things.
Though these machines cannot live a human life, their ability to use language, process information, and reason at speeds that far exceed our own—and with minimal human intervention—can amplify, augment, and accelerate human capabilities in profound ways. If only there was a field dedicated to understanding these capabilities. Wait. There is. Computer Science is the study of information, computation, and automation.
Computer Science is the study of information, computation, and automation.
Here’s a bit more on what this means:
One thing the Wikipedia definition doesn’t mention explicitly, but that is really important, is that these three things are tied together with language. Language allows us to express information, describe automated computations, and communicate both to other humans and to machines.
Understanding information, language, and computation is empowering. Without a good understanding of these topics, one runs the risk of losing agency and being controlled by those who do.
We kind of know, intuitively, what these three words signify, but to really understand them, we need to both study their histories and develop theories—organized bodies of knowledge with explanatory and predictive powers. A few great minds that have come before us have gotten us started. Let’s take a brief look at these important topics.
Theories are technicalInformation, language, and computation theories use a fair amount of logical and mathematical notation. If you need to brush up, see these logic notes and these math notes.
Let’s start with information.
First question: how do we represent (encode) information?
Here’s a first shot. A symbol is a primitive unit of information, and all information, we’ll postulate, can be represented with strings of symbols. Examples:
91332.3e-81
⚠️ Stay BEHIND the yellow line! ⚠️
<ul><li>你好</li><li>ᎣᏏᏲ</li><li>ᐊᐃᓐᖓᐃ</li></ul>
/Pecs/Los Angeles/Berlin/Madrid//1 2/3 0/2 2/2 0/3 2///
(* (+ 88 3) (- 9 57))
int average(int x, int y) {return (x + y) / 2;}
{"type": "dog", "id": "30025", "name": "Lisichka", "age": 13}
∀α β. α⊥β ⇔ (α•β = 0)
1. f3 e5 2. g4 ♛h4++
Now, where do the symbols come from?
The answer is you get to make them up.
We are going to explore Unicode. As we do, notice how each of the characters have a name. The name seems to suggest a meaning, but does it really? Do symbols have an inherent meaning? Or is it acquired somehow? If acquired, how so?
There is an established discipline called Information Theory that we won’t cover in depth here. You can take full-length college courses and read hundreds of books on this topic. It is huge! The genesis of the theory is attributed to Claude Shannon whose 1948 paper A Mathematical Theory of Communication is legendary—perhaps one of the most influential scientific papers of all time. Among the many contributions of the paper, Shannon:
Information Theory is used in the foundations of computer science for encoding data, ensuring reliable communication, optimizing and securing storage and transmission, and building machine learning models and systems. The world has generated a lot of stored and transmitted electronic information. How much? If interested, start at Wikipedia’s article on the Zettabyte Era.
There are many ways to describe what we mean by the word language. Here’s just one of many: In a TPWKY podcast appearance, Steven Mithen, author of The Language Puzzle, describes language as a system of communication comprised of discrete units with shared, often arbitrary meanings, combined using specific rules to generate and interpret complex thoughts.
How can we study language?
Our approach in this course will move deliberately from abstract to embodied then back again. We’ll look at language three ways, in this order:
We’ll be using the following domains of study for the three perspectives:
Language is formal, mathematical, and precise. Languages are sets of strings generated by rules.
Math
“Complete Precision”
Language has evolved under biological, historical, and social forces.
Culture
“Contingency and Improvisation”
Language is patterns emerging from neural networks using attention and transformers.
Engineering
“Pattern without Experience”
We’ll explore these three aspects in this order to create an arc from the precision of pure structure to the biological, cultural, and embodied aspects of rather messy evolutionary and anthropological linguistics, then back to the quantitative realm where language patterns emerge from data and training.
No single view is complete.The goal of the course is to leave you suspicious of any account of language—or of mind, or of thought, or of communication—that speaks from only one of these perspectives. Along the way we will encounter theories that were once widely accepted but are now considered outdated or incomplete.
If language is the expression of information, then computation is its processing.
The human understanding of, and application of, computing has evolved over millennia. It has arguably enhanced human capabilities. There is a history here that is empowering to know. History is an essential component of the study of any discipline. However:
[Computing is] complete pop culture. The big problem ... [is that] the electronic media we have is so much better suited for transmitting pop-culture content than it is for high-culture content. I consider jazz to be a developed part of high culture. [Jazz aficionados take deep pleasure in knowing the history of jazz, but] pop culture holds a disdain for history. Pop culture is all about identity and feeling like you're participating. It has nothing to do with cooperation, the past, or the future—it's living in the present. I think the same is true of most people who write code for money. They have no idea where [their culture came from]. —Alan Kay
Woah. He’s right. Let’s do something about this.
Here are some important events, people, works, and machines to be aware of in order to have a proper context for the study of computation.
For thousands of years, humans have been marking, matching, comparing, tallying, measuring, and reckoning. The Ishango bone, from around 20K years ago, was likely used for such purposes.
Early inventories featured pictorial markings like 🌽🌽🌽🌽🌽 🍓🍓🍓. Then someone figured out they could save space by writing something like 5🌽 3🍓. An incredibly powerful idea: symbols representing quantities!
Numerals!
Early numeral systems like the Egyptian system were additive, but in the modern era, positional systems have become universal.
Recipes, or lists of instructions, for manipulating quantities and measurements were sometimes recorded. Notable examples include:



While humans could follow recipes and carry out reckoning with their fingers and toes, or use tally marks on bones, gains in efficiency and accuracy naturally arose with the creation of mechanical devices for calculation. Here are just a few examples:




The Analytical Engine was fascinating historically. Although never built, it was perhaps the world’s first programmable, general-purpose computer. Several programs are known to have been written for the device, the most famous of which—a program for the computation of Bernoulli numbers—was presented by Babbage’s collaborator Ada Lovelace in her translator’s notes to Luigi Menabrea’s article in Taylor’s Scientific Memoirs in 1843. (Also see this shorter summary). Lovelace is often considered the world’s first programmer. She is also widely admired as being perhaps the first to recognize the wide application of computing beyond arithmetic, writing that the Analytic Engine
might act upon other things besides number, were objects found whose mutual fundamental relations could be expressed by those of the abstract science of operations, and which should be also susceptible of adaptations to the action of the operating notation and mechanism of the engine.... Supposing, for instance, that the fundamental relations of pitched sounds in the science of harmony and of musical composition were susceptible of such expression and adaptations, the engine might compose elaborate and scientific pieces of music of any degree of complexity or extent.
The term algorithm meaning a description of a computation comes from Al-Khwārizmī. People happily used computations for over a thousand years without a formal definition of what an algorithm actually was. To be fair, no one really needed one. But the need for such a definition did arise. In the 19th century, philosophers and mathematicians started making some major advances in their fields, breaking free of Aristotlean syllogistic thinking. Georg Cantor introduced us to the paradise of multiple infinities. People started discovering paradoxes and started not only worry about how to put mathematics on a rigorous, consistent footing, but also to try to define what math even was. Three philosophies emerged:
Math is just logic
Math is only what we invent and can construct or demonstrate
Math is done by manipulating symbols according to rules
Major works of this era include:




During this era, people started noticing inconsistencies and paradoxes popping up in mathematics, and started an effort to get mathematics on a solid foundation. This ultimately led to David Hilbert challenging mathematicians to show that all of mathematics could be built from an axiomatic formal system that was sound (everything derivable is true), complete (every truth is derivable), consistent (no contradictions can be derived), and decidable (an algorithm exists to determine a priori whether a statement could be derived in the system).
But it was not to be.
Kurt Gödel, in 1931, proved that any attempt at providing a consistent, formal, axiomatic basis for mathematics (that included arithmetic with multiplication) could never capture all truths. Formalized mathematics had, then, to be incomplete (or inconsistent, but that’s unacceptable). Gödel did not answer the decidability question, or Entscheidungsproblem. Hilbert thought mathetmatics was decidable, that a decision procedure for all of mathematics existed. Others did not. But to prove him wrong, a formalization of the very notion of an algorithm is required.
Three approaches of the 1930s to formalize the notion of an algorithm were all successful. They were:



Church and Turing both showed the Entscheidungsproblem had no solution. Poor Hilbert.
Story TimeWe‘ll go over the story of Church, Gödel, and Turing in class only. You can’t find everything on the notes!
Turing’s paper is one of the most impactful and significant of all time. In this work:
Turing is revered as the founder of computer science because of this work. The ideas that there are limits to computation, equivalent ways to express computation, and the existence of a truly universal computing machine make computer science a discipline. For now, watch this video, ignoring the misleading click-baity title (incompleteness is a feature, not a flaw):
In case your only previous exposure to Turing was the movie The Imitation Game, remember that movies aren’t history, and change events for dramatic effect. Here’s a brief video pointing out a few of the inaccuracies of the movie, some rather insulting to Turing himself, and to his many collaborators:
Though Turing’s work made multiple significant contributions to the theory of computation, the universal machine may have been the most impactful. On this machine, you could encode essentially any algorithm, then hand it off to the machine for execution. This is automation, an important pillar of computer science, and perhaps its most characteristic aspect.
Turing's Universal Machine (1936) opened up a flood of new work in general purpose machines. Wikipedia’s History of Computing Hardware page covers both ancient devices and a fair number of machines from the 1930s–1960s. It is worth a look to see how we eventually got to the electronic devices that are ubiquitous today.

What does an algorithm that can be handed to a machine for execution look like? For some machines, like the Turing machine, it is a table of state transitions. For others, it is a list of instructions, written as...numbers. How could anyone read such things? Maybe John von Neumann could: in fact, he once angrily asked “Why would you ever want more than machine language?” Today that question sounds silly. Assembly language (co-invented by Kathleen Booth) is too low-level for humans to write directly. One of the biggest proponents of high-level languages was Grace Hopper, who also happened to write the first compiler.
We now have a rich variety if languages with which to express computations. Let’s take a quick peek at a few, seeing how they sum the squares of even numbers from a collection. One approach is to explicitly iterate through the list, checking each element to see if it is even, and if so, squaring it and accumulating the square into the final answer. This is the approach we must take in Go:
func sumOfEvenSquares(a []int) int { total := 0 for _, x := range a { if x % 2 == 0 { total += x * x } } return total }
Another approach is to first filter (a.k.a. ”select”) the even numbers, then map the squaring operation over each of them, and finally reduce (a.k.a. “fold”) the plus operation over the filtered-and-squared values to produce the result. For example:
[3, -2, 9, 51, 20, 8, 0, -31, 10][-2, 20, 8, 0, 10][4, 400, 64, 0, 100]568Most major programming languages have these operations built-in. TypeScript and Java are two of them:
function sumOfEvenSquares(a: number[]): number { return a.filter(x => x % 2 === 0).map(x => x ** 2).reduce((x, y) => x + y, 0) }
int sumOfEvenSquares(int[] a) { return IntStream.of(a).filter(x -> x % 2 == 0).map(x -> x * x).sum(); }
Sometimes you can specify the filtering, mapping, and reducing functions without parameters, as in Haskell and Factor:
sumOfEvenSquares :: [Int] -> Int sumOfEvenSquares = sum . map (^ 2) . filter even
: sum-of-even-squares ( seq -- n ) [ even? ] filter [ sq ] map sum ;
Sometimes the filtering and mapping can be expressed in a single construct, as in Python::
def sum_of_even_squares(a): return sum(x**2 for x in a if x % 2 == 0)
And then there are languages with really, really powerful operators for processing arrays, as we see in K and Julia:
sumofevensquares: {+/x[&~x!2]^2}
function sumOfEvenSquares(a) sum((a[a .% 2 .== 0]).^2) end
Several thousand programming languages have been created over the years. Here are some notable ones:





Languages have to not only be expressive, but they have to be efficiently implementable. This is why we study compilers and interpreters. Two major parts of this field are:



People create programs for people. Areas with people-focus include:




Trends in the modern computing era:




Computing history is so much richer than the brief outline above. The following are highly recommended:

You can also take a few minutes to watch Jeff’s historical overview:
Computing is so much more than its technical core, its theoretical concepts, or even its history. Computing is a human activity. Computations, programming languages, and machines, are designed by humans for humans.
Grady Booch’s Computing: The Human Experience project has this to say:
The story of computing is the story of humanity: this is a story of ambition, invention, creativity, vision, avarice, power, and serendipity, powered by a refusal to accept the limits of our bodies and our minds. Computing: The Human Experience is a transmedia project that explores the science of computing, examines the connections among computing, individuals, society, and nations, and by considering the history of computing contemplates the forces that will shape its future.
The story of computing is “powered by a refusal to accept the limits of our bodies and our minds”
It is worth immersing yourself in this content for hours.
During humans’ creation of a science, art, and craft of computing, several major themes have emerged. They form the core of the field that we now call computer science. Among these themes, or big ideas, are:
In studying any discipline, humans create theories. A theory is an organized body of knowledge with explanatory and predictive powers. There are four major theories of computation:
Theories are important! Without them, understanding is fragmented, superficial, and lacks a vocabulary for effective communication. Theories are essential for both absorbing and creating new knowledge.
The practice of computing involves gaining proficiency in crafting computations (programs) in dozens of different programming languages and in many different programming styles, applying concepts, patterns, and idioms. It also includes the design and implementation of new programming languages and even new machines.
What is the best kind of practice to make the theory come alive, and build a deeper understanding of computation?
Answer: Designing one’s own language and implementing compilers and interpreters for it.
Language and computation are about expression and behavior. Both have static and dynamic aspects. How do they relate to the world of information, ideas, processes, and reality?
There is, in the universe—or at least in our conception of it—a kind of fault line between the denotational (what exists?) and the operational (how do we know or produce something?). Rather than approaching the topic through analytic philosophy, here is a rough table of complementary themes. Some of these will be debatable and perhaps slightly inaccurate, and maybe there’s some overlap and nuance, but hopefully you can tell what we’re aiming for.
| Denotational | Operational |
|---|---|
| What is it? | How does it work? |
| Being | Becoming |
| Truth | Proof |
| Mathematics | Computer Science |
| Declarative | Imperative |
| Statements | Commands |
| Existence | Construction |
| Models | Inference Rules |
| Classical Logic | Intuitionistic Logic |
| Dogma | Evidence |
| Faith | Reason |
| Platonism | Constructivism |
| Logos | Praxis |
| State | Transition |
| Objects (Democritus) | Processes (Heraclitus) |
| Substance | Function |
| Statics | Dynamics |
| Structure | Behavior |
| Data | Algorithm |
| Specification | Execution |
| Meaning | Evaluation |
| Constraints | Search |
| Functions as Sets | Functions as λ-calculus expressions |
| Languages as Sets | Grammars and Recognizers |
| Ontological | Phenomenological |
The study of computation forces us to confront these complementary themes and to understand how they interact with, oppose, and complement each other.
Here are some questions useful for your spaced repetition learning. Many of the answers are not found on this page. Some will have popped up in lecture. Others will require you to do your own research.

We’ve covered: