Take the 2-minute tour ×

Since programming languages initially only used lines of code executed sequentially, and it evolved into including functions which were one of the first levels of abstraction, and then classes and objects were created to abstract it even further; what is the next level of abstraction?

What's even more abstract than classes or is there any yet?

share|improve this question

closed as not constructive by MichaelT, Bart van Ingen Schenau, Martijn Pieters, Walter, GlenH7 May 6 '13 at 13:25


You appear to think that's a linear progression, with an ordering ("X is more abstract than Y is more abstract than Z"). I beg to differ. –  delnan Sep 4 '12 at 18:28
It would be a pretty good idea to say why you beg to differ. ;) (read: I'm interested!) –  Serg Sep 4 '12 at 20:07
I propose the ultimate "abstraction": Do what I think, not what I type. ;) –  Izkata Sep 4 '12 at 20:51
@Delnan: Abstraction does not introduce a total order, but it is possible to say "more abstract", "less abstract". For example, a generic implementation of merge sort is more abstract than an implementation of merge sort that only works for integers. –  Giorgio Mar 12 '13 at 9:25
Bros, learn you some category theory. Then we can discuss what abstraction really means like civilized men. –  davidk01 May 4 '13 at 7:41
show 5 more comments

10 Answers

up vote 31 down vote accepted

I think you have some misconceptions about the history of computing.

The first abstraction (in 1936) was, in fact, Alonzo Church's Lambda Calculus, which is the foundation for the concept of high-order functions and all of the functional languages that followed. It directly inspired Lisp (the second oldest high-level programming language, created in 1959), which in turn inspired everything from ML to Haskell and Clojure.

The second abstraction was procedural programming. It came out of the von Neumann computer architectures where sequential programs were written, one instruction at a time. FORTRAN (the oldest high-level programming language, 1958) was the first high-level language to come out of the procedural paradigm.

The third abstraction was probably actually declarative programming, first exemplified by Absys (1967), and then later Prolog (1972). It is the foundation of logic programming, where expressions are evaluated by matching a series of declarations or rules, rather than executing a series of instructions.

The fourth abstraction then was object-oriented programming, which made its first appearance in Lisp programs in the 60's, but was later exemplified by Smalltalk in 1972. (Though there seems to be some debate as to whether the message-passing style of Smalltalk is the One True object-oriented abstraction. I'm not going to touch that.)

All other abstractions, especially on the traditional von Neumann computer architecture, are variations on those four themes. I'm not convinced that there is another abstraction beyond those four that is not merely a variation or a combination of them.

But an abstraction is, in essence, merely a way to model and describe an algorithm. You can describe algorithms as a series of discrete steps, as a set of rules which must be obeyed, as a set of mathematical functions, or as interacting objects. It's very hard to conceive of any other way to describe or model algorithms, and even if there is, I'm not convinced of its utility.

There is, however, the quantum computing model. In quantum computing, new abstractions are necessary to model quantum algorithms. Being a neophyte in this area, I can't comment on it.

share|improve this answer
Could Aspect Oriented Programming be considered yet another form of abstraction? –  Shivan Dragon Sep 4 '12 at 19:05
Object-oriented programming came out of Lisp in the 60s? [citation needed], big time. Everything I've heard says that it came out of Simula in the 60s, which was a direct descendant of ALGOL, an imperative language that had nothing to do with Lisp. Smalltalk came about by taking concepts introduced by Simula and twisting them around to feel more "lispy". –  Mason Wheeler Sep 4 '12 at 19:05
@MasonWheeler: It's in the Lisp 1 and 1.5 manuals, and it's common to hear Lispers talk of doing object-oriented programs, especially the old guys. The AI guys were big on doing object systems in Lisp at the time. –  greyfade Sep 4 '12 at 19:09
@ShivanDragon: I'd say no. It's just a way of instrumenting an otherwise procedural program with additional harnesses. It doesn't really model algorithms, nor does it seem to influence the design of data structures. –  greyfade Sep 4 '12 at 19:10
Actually, the SKI combinator calculus predates both lambda calculus and the Turing Machine. –  Jörg W Mittag Sep 5 '12 at 1:39
show 7 more comments

For many, the purest form of code abstraction in the current era of binary programming is the "higher-order function". Basically, the function itself is treated as data, and functions of functions are defined, much as you would see them in mathematical equations with operators defining the result of their operands and a predetermined order of operations defining the "nesting" of these operations. Math has very very few "imperative commands" in its structure; the two examples I can think of are "let x have some value or be any value conforming to some constraint", and "piecewise functions" in which the input determines the expression needed to produce the output. These constructs are easily representable as their own functions; the "function" x always returns 1, and "overloads" of functions are defined in terms of what is passed to them (which unlike object-oriented overloads can be defined based on values input) allowing "piecewise" evaluation of a named group of functions, even in terms of themselves. As such, the program does away with the notion of imperatives at the low level, and instead focuses on "evaluating itself" given input data.

These higher-order functions form the backbone of "functional languages"; what a program does is defined in terms of "pure functions" (one or more inputs, one or more outputs, no side effects or "hidden state"), which are nested into each other and evaluated as necessary. In such cases, most "imperative logic" is abstracted away; the runtime handles the actual calling of functions, and any conditions in which one or the other overload of a function may need to be called. In such a program, the code isn't thought of as "doing" something, it's thought of as "being" something, and what exactly it is is determined as the program runs given initial input.

Higher-order functions are now a staple of many imperative languages as well; .NET's lambda statements basically allow for "anonymous" functional input into another "function" (implemented imperatively but theoretically it doesn't have to be), thus allowing for highly-customizable "chaining" of very general-purpose "functions" to achieve the desired result.

Another abstraction commonly seen in the latest round of programming languages is dynamic variable typing based on the concept of "duck-typing"; if it looks like a duck, swims like a duck, flies like a duck and quacks like a duck, you can call it a duck. Doesn't matter if it's actually a mallard or a canvasback. It MIGHT matter if it's actually a goose or a swan, but then again it might not matter if all you care about is that it swims and flies, and kinda looks like a duck. This is considered the ultimate in object inheritance; you don't care what it is, except to give it a name; what's more important is what it does. In such languages there are basically only two types; the "atom", a single element of information (one "value"; a number, character, function, whatever), and the "tuple", composed of an atom and a "pointer" to everything else in the tuple. Exactly how these types are implemented in binary by the runtime is irrelevant; using these, you can achieve the functionality of virtually every type you can think of, from simple value types to strings to collections (which, since values can be of different "types", allows for "complex types" aka "objects").

share|improve this answer
"Duck typing" (at least as you describe) is not really novel or limited to dynamically typed languages; it's been around for a long time in statically typed languages as "structural sub-typing", best seen in OCaml. –  Tikhon Jelvis Mar 20 '13 at 5:28
add comment

One could consider Domain Specific Languages like SQL as a higher order of abstraction. SQL is a very targeted language that abstracts away operations such as storage and provides higher level functions based on set theory. Also consider how many mainstream languages today do not target a specific architecture but rather a Virtual Machine (such as the JVM or the .NET CLR). For an example C# is compiled to IL which is interpreted (or more frequently JIT'd --Just In Time Compiled-- to a native implementation) by the native runtime engine.

There has been much hubbub about the concept of DSLs being used to create very high level languages that can be used without much technical experience to create a working program. Think if someone were able to describe their entities and interactions in close to plain English and the operating environment handled everything from presenting a simple UI, to storing data in a database of some kind. Once those operations become abstracted, you can imagine how effortless programming can become.

There are some in existence today such as JetBrains MPS (which is a toolkit for describing DSLs or a language generator). Microsoft had a brief foray (and very promising I might add) into this space with it's M language (the M language was so complete that the language was defined in M).

Critics of the concept point to prior failed attempts to remove programmers from the job of developing programs, the difference with the DSL workbenches (as Fowler calls them) is that, developers would still be involved in codifying concepts that the Domain Experts could use to express the needs of their domain. Just like OS and Language vendors create tools that we use for programming, we would use DSLs to provide tools for business users. One could imagine DSLs that describe the data and logic, while developers create interpreters that store and retrieve data and apply the logic expressed in the DSL.

share|improve this answer
add comment

I would argue that meta-structures, modules, frameworks, platforms, and services are all higher-level feature groupings than classes. My hierarchy of programming system abstractions:

  • services
  • platforms, solution stacks
  • frameworks
  • modules, packages
  • meta structures: metaclasses, higher order functions, generics, templates, traits, aspects, decorators
  • objects, classes, data types
  • functions, procedures, subroutines
  • control structures
  • lines of code

Meta-structures like metaclasses, higher order functions, and generics clearly add abstraction to basic classes, functions, data types, and data instances. Traits, aspects, and decorators are newer mechanisms for combining code features, and similarly 'accelerate' other classes and functions.

Even pre-object languages had modules and packages, so putting them above classes might be debatable. Bu they contain those classes and meta-structures, so I rank them higher.

Frameworks are the meatiest answer--they orchestrate multiple classes, meta-structures, modules, functions, and such in order to provide sophisticated high-level abstractions. And yet frameworks still operate almost entirely in the realm of programming.

Solution stacks or platforms generally combine multiple frameworks, subsystems, or components into an environment for solving multiple problems.

Finally, there are services--often deployed as Web or network services. These are architectures, frameworks, solution stacks, or application capabilities delivered as complete bundles. Their internals are often opaque, primarily exposing administrator, programming, and user interfaces. PaaS and SaaS are common examples.

Now, this progression may not be entirely satisfying, for a few reasons. First, it makes a neat linear progression or hierarchy of things that aren't perfectly linear or hierarchical. It covers some abstractions like "stacks" and service that are not entirely under developer control. And it doesn't posit any new magic pixie dust. (Spoiler: There is no magic pixie dust.)

I think it's a mistake to look just for new abstraction levels. All the ones I listed above have existed for years, even if they haven't all been as prominent or popular as they now are. And over those years, the abstractions possible at every level of coding have improved. We now have general-purpose, generic collections, not just arrays. We loop over collections, not just index ranges. We have list comprehensions and list filter and map operations. Many language's functions can have a variable number of arguments, and/or default arguments. And so on. We are increasing abstraction at every level, so adding more levels isn't a requirement for increasing the overall level of abstraction.

share|improve this answer
add comment

The next abstraction after classes are meta classes. It's that simple ;)


share|improve this answer
Metaclasses are the root objects of the inheritance hierarchy(ies) of the language. .NET's is Object. You can also think of interfaces as metaclasses; they define the interface of their inheritors independently from the inheritor's actual "parent" class. –  KeithS Sep 4 '12 at 20:04
@KeithS that is not what the word means in any context I've seen it - from CLOS to UML to C#. A metaclass is a a class whose instances are classes - a weak implementation is C#'s Type which gives reflective capabilities but not mutation ( you can't add a new method to MyType by saying typeof(MyType).Methods += new Method ( "Foo", (int x)=>x*x ) as you can in CLOS ) –  Pete Kirkham Mar 20 '13 at 10:59
add comment

I am surprised nobody has mentioned category theory.

The most fundamental unit of programming is the function which is based on types. Functions are usually denoted as f: A -> B, where A and B are types. If you put these things, which I'm calling types and functions, together in the right way you get something called a category. You don't have to stop at this point.

Take these things, categories, and ask yourself what would be the correct way to relate them to each other. If you do it right you get something called a functor which goes between two categories and is usually denoted as F: C -> B. Once again you don't have to stop.

You can take all functors and put them together in the right way and if you do things just right you start to wonder how to relate two functors to each other. At this point you get something called a natural transformation, mu: F -> G, where F and G are functors.

My knowledge at this point gets fuzzy but you can continue to do this and keep climbing the ladder of abstraction. Objects and classes don't even come close to describing how high you can climb up the abstraction ladder. There are many languages that can express the above concepts computationally and the most prominent of those languages is Haskell. So if you really want to know what abstraction is really about then go learn some Haskell or Agda or HOL or ML.

share|improve this answer
add comment

If I understand you correctly, your "ascending abstractions" can be thought of as increasingly large encapsulations of logic, mostly related to code reuse.

From specific instructions executed one after the other, we move to functions/subroutines, which encapsulate, or abstract, a logical grouping of instructions into a single element. Then we have objects, or modules, which encapsulate subroutines relating to a certain logical entity or category, so I can group all string operations under the String class, or all common math operations under the Math module (or static class, in languages such as C#).

So if that's our progression, what comes next? Well, I don't think you have a clear-cut next step. As others have answered, your progression applies only to imperative/procedural programming styles, and other paradigms don't share your concepts of abstraction. But if I there's something that can logically extend your metaphor, it's services.

A service is similar to a class in the sense that it's an entity that exposes functionality, but it implies a much stricter separation of concerns than the back-and-forth with objects you instantiated yourself. They expose a limited set of operations, hide the internal logic, and are not even necessarily running on the same machine.

Again, there is a fine distinction. In most cases, you will use an object that acts as a proxy to a service, and the two will be very similar, but as an architecture, the two are distinct.

share|improve this answer
add comment

New forms of abstraction hide low-level work from you. Named procedures and functions hide program addresses from you. Objects hide dynamic memory management and some type-dependent "if statements".

I'd suggest that the next level of practical abstractions that will hide low-level drudgery from you are those of functional reactive programming. Look at "signals" in something like http://elm-lang.org/ which hide the callbacks and update dependencies you would have to explicitly manage in javascript. FRP can hide a lot of the complexity of inter-process, and inter-machine communication that's needed in large-scale internet applications and high-performance parallelism too.

I'm pretty sure that this is what we're all going to get excited about in the next 5 years or so.

share|improve this answer
FRP is great, but it deals with a fairly specific sort of programming (namely reactive programming). It is not very good for modelling other sorts of programs. However, the more general sort of programming it represents--writing your code in terms of algebras--is a good candidate for a new level of abstraction. –  Tikhon Jelvis Mar 20 '13 at 5:24
add comment

Set theory -- as partially implemented in relational databases, but also in statistics languages like SAS and R, provide a different but arguably higher level of abstraction than OO.

share|improve this answer
add comment

I think the actor model is missing from the list of candidates.

Here's what I mean by Actors:

  • independent entities
  • that receive messages, and when receiving a message, may
  • create new actors,
  • update some internal state for the next message,
  • and send out messages

This model is something beyond deterministic Turing machines, and is actually closer to our real-world hardware when looking at concurrent programs. Unless you employ extra (costly) synchronization steps, these days, when your code receives data, that value may already have changed on the other side of the same die, maybe even inside the same core.

Short discussion/introduction: http://youtube.com/watch?v=7erJ1DV_Tlo

share|improve this answer
your post is rather hard to read (wall of text). Would you mind editing it into a better shape? –  gnat May 4 '13 at 14:28
add comment
