Computational Literacies Lab

0002.e Abstraction

On this page
  1. Hiding details to manage complexity
  2. Interface and implementation
  3. Layers: abstraction across a whole system
  4. Data abstraction

The function at the end of the last section, used by knowing only what it promises, is one example of an idea that runs through all of computing. Abstraction means hiding details behind a simpler interface, so that you can use something without knowing how it works. This section begins with that basic idea and the reason it matters, then gives its central distinction between an interface and an implementation. It then looks at abstraction at two other scales: the whole computer, built as a stack of layers, and data, where a representation hides the complexity of what it represents.

Hiding details to manage complexity#

You abstract whenever you call sort() to put scores in order without reading how it works, or drive a car with a steering wheel and pedals while knowing little about the engine.1 A web developer can call fetch(url) without thinking about how packets are routed. Writing your own abstractions has the same effect: a student who wraps forward(100) and right(90) in a function square(int size) can start thinking about a row of houses without keeping track of each turn. Abstraction differs from decomposition, though the two work together. Decomposition divides a whole into parts, and abstraction decides what each part shows to the outside.

Abstraction's main benefit is that it lets people reason about one level at a time, which makes large systems possible to build and understand. No engineer could keep an entire operating system in mind, so each of the 50 engineers on a project works on one component and relies on what the others promise, without reading their internal code. The same habit helps in debugging, since you can check whether move_player() returns the correct position and look inside only if it doesn't. It is also why a 5,000-line program in which everything shares the same global variables is so fragile: without boundaries, any change can break something unrelated, and organizing the code into functions with defined inputs and outputs limits how far a change can reach.

This shouldn't be mistaken for making details go away. Abstraction doesn't remove details, and details can still matter: a hidden algorithm may be slow on large inputs, and you'll find that out only when it affects your program. Nor does it end the need for people to communicate or stop every bug from crossing a boundary between components.

Interface and implementation#

The boundary that makes all of this work has two sides. An interface states what something does, and an implementation states how. An implementation can change without affecting callers if inputs and outputs stay the same.2 A library's search(String[] items, String target) can move from scanning an array to using an index for faster lookups, and programs that call it continue to work unchanged. A class's area() method can calculate from a stored width and height or return a stored area, and callers writing shape.area() can't tell the difference. This separation also lets people work independently: if one team is building a page and another is building get_price(int product_id), they need to agree on the function's name, parameter, and return type, and can leave everything else to the team doing the work.

The arrangement places an obligation on callers as well, because they should rely only on what the interface promises. Suppose a function is documented as returning a list "in no particular order," but it has always happened to return sorted values. If a program depends on that order and breaks when the implementation changes, the fault lies with the caller, who relied on something that was never promised.

Layers: abstraction across a whole system#

Interfaces can be stacked, each one built from what the one beneath it provides, and that is how computers are organized. From low to high, the main layers of abstraction are transistors, logic gates, machine code, the operating system, high-level languages, and applications. Each relies on the one below. When a word processor saves a file, it contains no code about how the disk stores individual bits, because the operating system provides file services that hide those details. When a Python statement eventually results in electrical signals, there's no direct link between the two, since the statement passes down through layers, each translating into or relying on the one below it, until hardware does the work.

The layers show the value of a stable interface especially clearly at the boundary between software and hardware. An instruction set, the set of machine instructions a processor accepts, is an interface between software and hardware. This is why new chips can run old programs: a manufacturer can completely redesign a chip's internals, and existing machine code still runs, as long as the instruction set is preserved.

Data abstraction#

Everything so far has hidden processes, but data can be abstracted too. Data abstraction means representing complex information through simpler structures, keeping what the task needs and ignoring the rest. A Book class for a school library might store title, author, and availability, but not paper type or weight.3 A budgeting program needs a customer's name and balance, and can omit hair color. It can't omit so much that it no longer identifies the account, which is the same judgment about relevant features that a model requires in 0002.a. Structures themselves are abstractions as well: a list of scores stands for a student's work, and a two-dimensional array lets a program handle a chessboard's squares by position in a single structure, instead of through dozens of separately named variables.

Data abstraction involves two related decisions. The first is what to represent at all, as with Book. The second is whether other code can see how it is represented. Hiding the representation behind methods lets it change later: if a student's scores sit in a float[] and other code gets the average through a method, then that code doesn't depend on the array, and the array can be replaced. This second decision is the interface and implementation distinction again, applied to data.

Abstraction lets you stop thinking about what is inside a function, but someone has to write the inside. The next section opens that box and looks at the small set of structures from which algorithms are built.

  • abstraction: hiding details behind a simpler interface
  • interface: what something does, and what callers can rely on
  • implementation: how it does it
  • layers of abstraction: from low to high, transistors, logic gates, machine code, operating system, high-level languages, applications
  • instruction set: the interface between software and hardware
  • data abstraction: representing complex information through simpler structures that keep what the task needs

Practice: practice questions for 0002.e

  1. MWC1, Names: variables, values, and functions with arguments. ↩

  2. MWC2, Riddle Server: views, models, fields, subclasses, and an API specification. ↩

  3. MWC1, Dice: classes, instances, randomness, and repeated runs. ↩