Abstraction

Neil Ernst/Thomas Latoza

2025-09-18

Abstractions

Based on original notes from Thomas Latoza (GMU).

What is an Abstraction?

The ability to interact with an idea while safely ignoring some of its details. A set of operations on shared state that make solving problems easier. Examples:

  • Data: String, Integer, ComplexNumber
  • Collections: array, list, stack, queue, map, set, …
  • Concurrency: Actors, semaphores, promises,
  • AI: TensorFlow, PyTorch
  • Web: HTTP verbs (Get/Put/Delete)
  • Business: Person, Customer, AnimalSighting

Benefits

  • communication: Abstractions (and as we will see, design patterns, implementation styles, architectural tactics …) are a communication mechanism and a shorthand.
  • shared abstractions: allow for quicker and more effective understanding of the code.
  • naturalness: Some abstractions might come directly from the domain (e.g., a Customer class) (all of OO is arguably premised on this idea)
  • simplify: hide complexity and implementation details
  • interoperability: pass data around e.g. in JSON

All models are wrong; some are useful. (George Box)

Activity: List API

WriteGet LLM to write a function to reverse a list with element-wise operations

  • Node.getNext()
  • Node.setNext()

Activity

Now write the same function for a list with list operations

  • List.get(i)
  • List.set(i)
  • List.remove(i)

Activity

Finally, write the function with element operations

  • Node.getNext()
  • Node.setNext()
  • Node.getPrev()
  • Node.setPrev()

Abstractions in Each Example

For each solution, write out the key abstractions being used and how that impacts your solutions.

Write your names on the paper and hand it in so I can review.

What does Java do in this case?

  • depends on the abstraction and implementation (ArrayList vs LinkedList)
  • the interface List thus does not specify reverse() (why?)

Lists

Many different operations and syntactic formulations (list comprehensions, direct access, function access).

Like all abstractions, hides some irrelevant information from the user (in this case, how it is stored on disk).

Good abstraction leaves implementation details to talented developer (e.g. Java collections framework).

Example: Promises

Concurrency is a well known challenge for people to comprehend. One tricky part in Javascript (among others) has been the idea of callbacks. A callback is something like

function myfunc(callback, param1) {
    //do stuff
    callback(result);
}

And the advantage is it can execute asynchronously. But you can end up with long nested chains of callbacks, making this abstraction hard to parse. (why?)

Callback hell

doSomething(function (result) {
  doSomethingElse(result, function (newResult) {
    doThirdThing(newResult, function (finalResult) {
      console.log(`Got the final result: ${finalResult}`);
    }, failureCallback);
  }, failureCallback);
}, failureCallback);

Example: Promises

A new abstraction called Promise was introduced to manage this chaining.

let myPromise = new Promise(function(myResolve, myReject) {
    // slow code (e.g. network access)

    myResolve(); // when successful
    myReject();  // when error
});

And then the caller uses a Promise consumer:

myPromise.then(
  function(value) { /* code if successful */ },
  function(error) { /* code if some error */ }
);

and the promise knows to call the appropriate function.

Promise Advantages

The big advantage is chaining Promises, so we avoid deeply nested callback chains. Instead, we get some guarantees of how the Promises return, and can attach a single error handler.

Promises

doSomething()
  .then((result) => doSomethingElse(result))
  .then((newResult) => doThirdThing(newResult))
  .then((finalResult) => {
    console.log(`Got the final result: ${finalResult}`);
  })
  .catch(failureCallback);

There are more reasons to use Promises, and their evolution, async/await, but this covers the abstraction and what it enables.

Note the development here: blocking functions –> asynchronous callbacks –> Promises –> Async/Await –> …

Good Abstractions

  • Should do one thing and do it well
    • If hard to name, that’s a bad sign
  • Implementation should not leak into abstraction
    • If there’s details that do not need to be exposed, do not
  • Names matter
    • Be self-explanatory, consistent, regular

Challenges

  • What operations to include? (a.ka., interface)
    • Choices of operations has many consequences
  • How to version your abstraction? Abstractions get stale.
  • How does your abstraction fit with others?

Caveat: Leaky Abstractions

Think about our List example. Where could our abstraction get us into trouble?

A leaky abstraction is an abstraction in which the hidden details can leak out of the abstraction boundaries. For example, thinking of time as a single, 24h cycle without understanding time zones or leap seconds. Or not understanding that the world isn’t ASCII-based.

Summary

  • Good design is about abstraction:
    • when to use them, what to call them, how to compose them
  • abstractions allow designers to ignore implementation specifics
  • abstractions help us communicate ideas