Classification of the principal programming paradigms

Print this figure as a poster!

Peter Van Roy
Department of Computing Science and Engineering
Université catholique de Louvain (UCL) à Louvain-la-Neuve

Programming Paradigms for Dummies: What Every Programmer Should Know (article)

This article appears as a chapter in New Computational Paradigms for Computer Music, G. Assayag and A. Gerzso (eds.), IRCAM/Delatour France, 2009. The article presents and explains the most recent version of the poster.

Paradigms poster in French

Explanation of the chart

This chart is inspired by Concepts, Techniques, and Models of Computer Programming (MIT Press, 2004). This textbook presents many programming paradigms using the Oz multiparadigm programming language for its examples. A programming paradigm is an approach for programming a computer that is defined by a specific set of programming concepts and techniques, as embodied by its kernel language, the small core language in which all the paradigm's abstractions can be defined. A multiparadigm language allows programming in each of many paradigms without interference from the others. Multiparadigm programming is a natural approach to programming: it allows to use the concepts necessary for a program without being encumbered by unnecessary concepts.

The chart classifies programming paradigms according to their kernel languages. Kernel languages are ordered according to the creative extension principle: a new concept is added when it cannot be encoded with only local transformations. Two languages that implement the same paradigm can nevertheless have very different "flavors" for the programmer, because they make different choices on what programming techniques and styles to facilitate. All paradigms that support functional programming can also support object-oriented programming.

When a language is mentioned under a paradigm, it means that part of the language is intended (by its designers) to support the paradigm without interference from other paradigms. It does not mean that there is a perfect fit between the language and the paradigm. It is not enough that libraries have been written in the language to support the paradigm. The language's kernel language should support the paradigm. When there is a family of related languages, usually only one member of the family is mentioned to avoid clutter. The absence of a language does not imply any kind of value judgment.

State is the ability to remember information, or more precisely, to store a sequence of values in time. Its expressive power is strongly influenced by the paradigm that contains it. We distinguish four levels of expressiveness, which differ in whether the state is unnamed or named, deterministic or nondeterministic, and sequential or concurrent. The least expressive is functional programming (threaded state, e.g., DCGs and monads: unnamed, deterministic, and sequential). Adding concurrency gives declarative concurrent programming (e.g., synchrocells: unnamed, deterministic, and concurrent). Adding nondeterministic choice gives concurrent logic programming (which uses stream mergers: unnamed, nondeterministic, and concurrent). Adding ports or cells, respectively, gives message passing or shared state (both are named, nondeterministic, and concurrent). Nondeterminism is important for real-world interaction (e.g., client/server). Named state is important for modularity.

Axes that are orthogonal to this chart are typing, aspects, and domain-specificity. Typing is not completely orthogonal: it has some effect on expressiveness. Aspects should be completely orthogonal, since they are part of a program's specification. A domain-specific language should be definable in any paradigm (except when the domain needs a particular concept).

Metaprogramming is another way to increase the expressiveness of a language. The term covers many different approaches, from higher-order programming, syntactic extensibility (e.g., macros), to higher-order programming combined with syntactic support (e.g., meta-object protocols and generics), to full-fledged tinkering with the kernel language (introspection and reflection). Syntactic extensibility and kernel language tinkering in particular are orthogonal to this chart. Some languages, such as Scheme, are flexible enough to implement many paradigms in almost native fashion. This flexibility is not shown in the chart.