Base CS from zero
What abstraction is
Every lesson so far has taken something apart. You saw a number split into bits, an instruction split into an opcode and operands, a function call split into a pushed stack frame with a saved return address. Taking things apart is how you learned what they really are.
But no working programmer holds all of that in their head at once. When you write
total = add(a, b), you do not re-derive how the CPU adds two numbers. You do not picture
the stack frame being pushed. You just write add and trust it.
That trust has a name: abstraction. It is the single idea that lets a program grow past a few lines without becoming impossible to think about. You have been using abstractions for nine units. This lesson finally names the thing and shows you its two halves — the part you use and the part you ignore.
After this lesson you can define abstraction as hiding detail behind a name, explain the difference between an interface and an implementation, point to a function call and a variable name as abstractions you have already used, and say why hiding detail makes a program easier to think about.
Abstraction: a name that hides detail. An abstraction is a name (or a small set of names) that lets you use a thing without knowing how the thing works inside.
The word names a deal you make. You give up direct knowledge of the inner detail. In return you get something short and stable to refer to. After the deal, you think about the name, not the machinery behind it.
You have already made this deal many times. In Unit 08 you called a function add(a, b)
and got back a sum. You did not, at the call site, think about how add produced that
sum. The name add stood in for all of that detail. That substitution — name in place of
detail — is abstraction, and it is the only reason a large program is writable at all.
The interface: what you use. Every abstraction has two sides. The first is the interface: the small, visible surface you interact with — the name, the inputs it expects, and the result it gives back.
For the function add, the interface is exactly this: the name add, the fact that it
takes two numbers, and the fact that it returns their sum. That is everything a caller
needs. Recall from Unit 08 lesson 03 that a function’s parameters are its declared inputs
and its return value is the result it hands back — together those are the interface of a
function.
The interface is a contract. It is a promise: “give me these inputs, and I will give you back this result.” A caller relies on the promise and nothing else.
The implementation: the hidden how. The second side is the implementation: the actual instructions, variables, and steps that carry out what the interface promises.
For add, the implementation is the function body — the machine instructions that
actually compute the sum. The caller never looks at the body. It is hidden behind the
name.
The key property: the implementation can change completely without the caller noticing,
as long as the interface keeps its promise. add could compute the sum with one
instruction or with ten; it could be rewritten entirely. Every caller still writes
add(a, b) and still gets the sum. The interface stayed fixed; the implementation was
free to change. That separation — fixed interface, replaceable implementation — is what
makes abstraction useful, not just tidy.
Why this works
Why a variable name is also an abstraction. Recall from Unit 06 that a variable is a
name for a memory cell. When you write score = 10, you never type the cell’s numeric
address. The name score is an abstraction: its interface is the name and the value
you can read or write; its implementation is the actual address the runtime chose. If the
runtime placed score at a different address, your code would not change. You have been
relying on this abstraction since Unit 06 without calling it one.
Hiding detail is what makes a program thinkable. Why bother separating interface from implementation? Because a human can only hold a few things in mind at once.
If using add forced you to also think about its instructions, and using those
instructions forced you to think about the gates beneath them, every line of code would
drag the whole machine along with it. No program of any size could be written.
An abstraction cuts that chain. Once add works and its interface is fixed, you can use
add while thinking only about the interface — two numbers in, a sum out. The
implementation still exists and still runs, but it is no longer in your head. You have
one thing to think about (add) instead of hundreds (every instruction inside it).
That reduction is the entire payoff, and the rest of this unit is about applying it on a
larger scale.
Splitting one function into interface and implementation.
Here is a tiny function and its caller:
function half(n: number): number {
let result = n / 2;
return result;
}
let x = half(10); // x becomes 5Find the interface. What does the caller on the last line actually depend on?
- The name
half. - That it takes one number.
- That it returns that number divided by two.
That is the whole interface — three facts. The caller writes half(10) and expects 5.
Find the implementation. What is hidden from the caller?
- The local variable
resultinside the body. - The fact that the division happens before the return.
- The exact instruction the CPU uses to divide.
The caller depends on none of that. result could be renamed, the two body lines could
be merged into return n / 2, and the caller would not change by one character.
The test. Change the implementation, keep the interface: does the caller still work?
Rewrite the body as a single line return n / 2. The caller half(10) still returns 5.
The interface held; the implementation moved. That is abstraction working exactly as
designed.
Common mistake
A common mistake is thinking abstraction means the detail disappears. It does not. The
implementation of add still runs on the CPU every single time you call it — every gate
still switches. Abstraction hides the detail from your attention, not from the machine.
The machinery is still there, still doing the work. You have simply been given a name so
you do not have to look at it.
An abstraction has two sides. The side you interact with — the name, the inputs, the result — has a name. Type 1 if that side is called the interface, type 0 if it is called the implementation.
A function add(a, b) returns a sum. Its body is rewritten to compute the sum a different way, but it still returns the same sum for the same inputs. How many callers of add must be changed?
When you write score = 10, how many numeric memory addresses do you type in your code?
A function's interface is made of its name, its declared inputs, and its return value. The function half takes 1 number and returns 1 number. Counting the name plus the inputs plus the return value, how many parts does its interface have?
True or false: when an abstraction hides a detail, that detail stops running on the CPU. Type 1 for true, 0 for false.
What is the difference between an interface and an implementation?
Abstraction is hiding detail behind a name so you can use a thing without knowing how it works inside. Every abstraction has two sides. The interface is the visible surface a user depends on — for a function, its name, its inputs, and its return value; it is a contract, a promise. The implementation is the hidden machinery that fulfils that promise — the function body and the instructions it runs. The implementation can be rewritten freely as long as the interface keeps its promise, so callers never change. You have used abstractions since Unit 06: a variable name hides a memory address, and a function call hides a body. Abstraction does not delete detail — the machine still runs it — it removes the detail from your attention, which is what lets a program grow large enough to be useful.