In this tutorial, we’ll study orthogonality and its application in computer programming.
The word ‘orthogonal’ comes from two ancient Greek words: ‘orthós’ meaning upright and ‘gōnía’ meaning angle.
It finds wide application in many areas such as mathematics, physics, signal processing, communication, computer programming, and even arts. But the concept of orthogonality means different things in each field.
In computer science, the general idea behind orthogonality is the ability to change one thing without any unseen effect on other things. In other words, it’s about the independence of components in a larger system.
2. Orthogonality in Programming Languages
We measure a language’s orthogonality by the degree to which we can combine its built-in constructs without side effects. In an ideally orthogonal language, no construct or language feature we use has any effect on how we use any other construct or a feature.
Based on this, a programming language belongs to one of the following three classes:
- Fully orthogonal
- Partly or quasi-orthogonal
The following diagram shows this classification with some examples of each class:
2.1. Fully Orthogonal Languages
A programming language is fully orthogonal if we can combine the majority of its constructs in many ways without any side effects. We consider Scala a fully orthogonal language.
For example, in Scala, we can use functions just like all other objects: as local variables, fields, or parameters to other functions. In doing so, we experience no side effects. For instance, passing a function as an argument doesn’t invoke it. Scala is orthogonal in both object-orientated programming and functional programming paradigms. So, we can freely adopt both of these frameworks in Scala without worrying about any side effects.
2.2. Quasi-Orthogonal Languages
We say a programming language is quasi or partly orthogonal if many of its basic constructs, but not the majority thereof, can be combined in only a few ways without side effects. Java and Python are such languages.
In Python, we can combine built-in data structures such as lists and sets to build advanced data structures. However, the constructor list doesn’t have side effects, whereas set does. For example, applying set to a list removes all duplicate values from the list. So, list(set(x)) isn’t the same as set(list(x)). As a result, those two built-in types aren’t orthogonal.
In Java, the accessibility specifiers such as public and private are completely orthogonal to the specifier static. However, the way variables are stored depends on their types. So, specifying a variable’s type has side effects. For example, in the following code, x is a value on the stack whereas y is a reference to the object’s content in the heap:
int x; /* x is a value */
MyClass y; /* y is a reference */
2.3. Non-orthogonal Languages
We categorize a programming language as nonorthogonal when most of its constructs can’t be combined in many ways without any side effects.
In simple terms, non-orthogonality means there are exceptions to the general language rules and syntax.
For example, we consider C++ as a non-orthogonal language.
In C++, we can declare an object to be of any fundamental type except void, but this doesn’t apply to templates. For example, for the following template declaration, we need to explicitly cast T as void for it to work:
template<typename T> T f()
T = void;
That doesn’t mean there’s no orthogonality in C++. Instead, only a few built-in constructs are orthogonal to one another.
Another example is C. For instance, it has two built-in data structures: arrays and structs. We can return a struct from a function in C, but cannot return an array from a function. Similarly, a struct can have members of any type except void or a structure of the same type, arrays can’t be of type void, and so on.
2.4. Consequences of Orthogonality
A very high level of orthogonality makes the language simple and easy to learn.
However, it can result in many ways to combine language constructs. Usually, that makes the compiler or interpreter very complex.
In addition to this, the features that are back-compatible should be orthogonal, i.e., it shouldn’t matter which version of a feature we use. As a result, the development of such languages is more complex.
3. Orthogonality in Software Design
Orthogonality is one of the most important requirements to make any software design 3C (Complete, Concise, Correct) compliant. A design or an architecture is orthogonal if and only if its operations don’t have side effects or after-effects.
So, every action, such as an API call, a callback, invocation of a background thread, or a language operation, changes just one thing without affecting others.
Let’s say we have an authentication server and 3 client applications. Orthogonality would mean the authentication request generated by one client are handled by the server without having any effect on the requests sent by other clients. In essence, orthogonal systems have one and only one way to change any property of their underlying subsystems.
As a result, orthogonality in software design reduces testing and development time because it’s easier to verify designs that neither cause side effects nor depend on each other’s actions.
3.1. Example of an Orthogonal System
Let’s consider a system having the following three parts:
Frontend gives a user interface for the end user to interact with the system. The database stores all the data associated with the users. The backend implements all the services for the user:
Let’s say the user wants to do an advanced search in his view history. Here, we can change the UI interface with new screens without affecting both the background search service and the database. We can also switch the database from Oracle to MySQL without affecting the UI and backend (minimal changes to write interface code). Furthermore, we can change the underlying search algorithm in the backend without affecting both UI and Database.
3.2. Example of a Non-Orthogonal System
Let’s consider the same system as above but with one difference. The backend uses a database’s stored procedure to carry out an advanced search while making queries. So the backend search service acts as a wrapper around the database’s stored procedure doing an advanced search:
In this case, we cannot replace the database without affecting the backend and there is no clear interface between the two. Any change in the database will have an effect on the entire system.
4. Orthogonality in Data Storage
From a data storage point of view, we usually value persistent and fault-tolerant systems. The length of time data is kept intact in a storage system is called its persistence.
In the storage domain, orthogonal persistence refers to a situation where a developer can fetch and process data irrespective of its storage time. In other words, the length of time data has been kept in orthogonal storage doesn’t affect the way we handle it.
In this article, we talked about orthogonality in computer programming. Orthogonality is a relationship between two things such that they do not affect one another.
Orthogonal systems ensure that effects of modification of one part of the system neither create nor propagate any side effects to other parts of the system. That way, communication between different components of the system is strictly controlled by well-defined interfaces.