Pyramid of doom (programming)

Last updated

In computer programming, a common challenge facing systems programmers is that before an operation can be performed, a number of conditions must first be checked to confirm that the operation can be successfully performed. For example, before data can be written to a file, it must be confirmed that 1) the program has the file open for writing; 2) the program has the necessary permissions to write the data; 3) the program has provided the data to be written; 4) the data to be written is of a valid size. A failure at any of these steps means that the write operation cannot be completed and an error should be returned to the calling program.

Contents

There are several ways that these multiple required tests can be written in the source code. One way is to check each condition in turn and if a condition fails, return from the subroutine at that point, indicating an error condition exists. This style of coding has the disadvantage that the subroutine returns from multiple (possibly many) points and some coding standards discourage having multiple return points.

Another way to is to check each condition and if the condition succeeds, enter a deeper block of code that checks the next condition and so on. The deepest enclosing block of code is only reached if all of the precondition tests are successful. This style of coding has the disadvantage that the indentation level increases with every test performed. If many tests are required, the enclosed blocks of code can march off the page to the right margin. This typographical effect is referred to as the pyramid of doom.

For example, the pyramid of doom is commonly seen when checking for null pointers or handling callbacks. [1] Two examples of the term are related to a particular programming style in early versions of JavaScript, [2] and the nesting of if statements that occurs in object-oriented programming languages when one of the objects may be a null pointer. [3] [4]

Examples

Most modern object-oriented programming languages use a coding style known as dot notation that allows multiple method calls to be written in a single line of code, each call separated by a period. For instance:

theWidth=windows("Main").views(5).size().width();

This code contains four different instructions; it first looks in the collection of windows for a window with the name "Main", then looks in that window's views collection for the 5th subview within it, then calls the size method to return a structure with the view's dimensions, and finally calls the width method on that structure to produce a result that is assigned to a variable name theWidth.

The problem with this approach is that the code assumes that all of these values exist. While it is reasonable to expect that a window will have a size and that size will have a width, it is not at all reasonable to assume that a window named "Main" will exist, nor that it has five subviews. If either of those assumptions is wrong, one of the methods will be invoked on null, producing a null pointer error.

To avoid this error, the programmer has to check every method call to ensure it returns a value. A safer version of the same code would be:

ifwindows.contains("Main"){ifwindows("Main").views.contains(5){theWidth=windows("Main").views(5).size().width();//more code that works with theWidth}}

If the programmer wishes to use that value based on whether or not it exists and is valid, the functional code inside the if statements is all pushed to the right, making it difficult to read longer lines. This often leads to attempts to "flatten" the code:

ifwindows.contains("Main"){theWindow=windows("Main")}iftheWindow!=null&&theWindow.views.contains(5){theView=theWindow.views(5)}iftheView!=null{theWidth=theView.size().width();//additional code}

Or alternatively:

if!windows.contains("Main"){// handle error}elseif!windows("Main").views.contains(5){// handle error}else{theWidth=windows("Main").views(5).size().width();//more code that works with theWidth}

This sort of programming construct is very common and a number of programming languages have added some sort of syntactic sugar to address this. For instance, Apple's Swift added the concept of optional chaining in if statements [5] while Microsoft's C# 6.0 and Visual Basic 14 added the null-conditional operators ?. and ?[ for member access and indexing, respectively. [6] [7] [8] JavaScript added support for the optional chaining operator in 2020. [9] The basic idea is to allow a string of method calls to immediately return null if any of its members is null, so for instance:

theWidth=windows("Main")?.views(5)?.size.width;

would assign null to theWidth if either "Main" or the fifth subview is missing, or complete the statement and return the width if they are both valid. There are many times where the programmer wants to take different actions in these two cases, so Swift adds another form of syntactic sugar for this role, the if let statement, also known as "optional binding":

iflettheView=windows("Main")?.views(5){//do things knowing the view exists...theWidth=theView.size.width}

Resolution

Pyramid of Doom can usually be resolved in any language by simply breaking up the code into multiple nested functions (or other groupings). For instance, instead of:

main(){aaaaa(){bbbbb(){ccccc(){ddddd(){// do something now}}}}}

You can break up the functionality like this:

doSomething(){// do something now}CC(){ccccc(){ddddd(){doSomething()}}}main(){aaaaa(){bbbbb(){CC()}}}

Similarly, data structures can be broken up by levels, when similar pyramids occur.

Not only is the Pyramid of Doom solved, but it's better practice to not have large, complicated functions; smaller ones are easier to get right, and easier to read, and verify the operation of. Choosing function names for each of these levels will also help the author clarify to readers what is done where. Typically, each level doesn't need many connections to levels that are far away, so separating them out is easy. If there are such connections, the author can re-think their design to something more reliable, because this is a fertile source of bugs.

See also

Related Research Articles

<span class="mw-page-title-main">Dylan (programming language)</span> Multi-paradigm programming language

Dylan is a multi-paradigm programming language that includes support for functional and object-oriented programming (OOP), and is dynamic and reflective while providing a programming model designed to support generating efficient machine code, including fine-grained control over dynamic and static behaviors. It was created in the early 1990s by a group led by Apple Computer.

VBScript is a deprecated programming language for scripting on Microsoft Windows using Component Object Model (COM) based on classic Visual Basic and Active Scripting.

Hungarian notation is an identifier naming convention in computer programming in which the name of a variable or function indicates its intention or kind, or in some dialects, its type. The original Hungarian notation uses only intention or kind in its naming convention and is sometimes called Apps Hungarian as it became popular in the Microsoft Apps division in the development of Microsoft Office applications. When the Microsoft Windows division adopted the naming convention, they based it on the actual data type, and this convention became widely spread through the Windows API; this is sometimes called Systems Hungarian notation.

In computer programming, specifically when using the imperative programming paradigm, an assertion is a predicate connected to a point in the program, that always should evaluate to true at that point in code execution. Assertions can help a programmer read the code, help a compiler compile it, or help the program detect its own defects.

In computer programming, indentation style is a convention, a.k.a. style, governing the indentation of blocks of source code. An indentation style generally involves consistent width of whitespace before each line of a block, so that the lines of code appear to be related, and dictates whether to use space or tab characters for the indentation whitespace.

In computer programming, named parameters, named-parameter arguments, named arguments or keyword arguments refer to a computer language's support for function calls to clearly associate each argument with a given parameter within the function call.

<span class="mw-page-title-main">Boolean data type</span> Data having only values "true" or "false"

In computer science, the Boolean is a data type that has one of two possible values which is intended to represent the two truth values of logic and Boolean algebra. It is named after George Boole, who first defined an algebraic system of logic in the mid 19th century. The Boolean data type is primarily associated with conditional statements, which allow different actions by changing control flow depending on whether a programmer-specified Boolean condition evaluates to true or false. It is a special case of a more general logical data type—logic does not always need to be Boolean.

Managed Extensions for C++ or Managed C++ is a deprecated set of language extensions for C++, including grammatical and syntactic extensions, keywords and attributes, to bring the C++ syntax and language to the .NET Framework. These extensions were created by Microsoft to allow C++ code to be targeted to the Common Language Runtime (CLR) in the form of managed code, as well as continue to interoperate with native code.

<span class="mw-page-title-main">C Sharp (programming language)</span> Programming language

C# is a general-purpose high-level programming language supporting multiple paradigms. C# encompasses static typing, strong typing, lexically scoped, imperative, declarative, functional, generic, object-oriented (class-based), and component-oriented programming disciplines.

In Microsoft Windows applications programming, OLE Automation is an inter-process communication mechanism created by Microsoft. It is based on a subset of Component Object Model (COM) that was intended for use by scripting languages – originally Visual Basic – but now is used by several languages on Windows. All automation objects are required to implement the IDispatch interface. It provides an infrastructure whereby applications called automation controllers can access and manipulate shared automation objects that are exported by other applications. It supersedes Dynamic Data Exchange (DDE), an older mechanism for applications to control one another. As with DDE, in OLE Automation the automation controller is the "client" and the application exporting the automation objects is the "server".

C# and Visual Basic (.NET) are the two main programming languages used to program on the .NET framework.

Nullable types are a feature of some programming languages which allow a value to be set to the special value NULL instead of the usual possible values of the data type. In statically typed languages, a nullable type is an option type, while in dynamically typed languages, equivalent behavior is provided by having a single null value.

TypeScript is a free and open-source high-level programming language developed by Microsoft that adds static typing with optional type annotations to JavaScript. It is designed for the development of large applications and transpiles to JavaScript. Because TypeScript is a superset of JavaScript, all JavaScript programs are syntactically valid TypeScript, but they can fail to type-check for safety reasons.

This article describes the syntax of the C# programming language. The features described are compatible with .NET Framework and Mono.

Windows Runtime (WinRT) is a platform-agnostic component and application architecture first introduced in Windows 8 and Windows Server 2012 in 2012. It is implemented in C++ and officially supports development in C++, Rust/WinRT, Python/WinRT, JavaScript-TypeScript, and the managed code languages C# and Visual Basic (.NET) (VB.NET).

PL/SQL is Oracle Corporation's procedural extension for SQL and the Oracle relational database. PL/SQL is available in Oracle Database, TimesTen in-memory database, and IBM Db2. Oracle Corporation usually extends PL/SQL functionality with each successive release of the Oracle Database.

In programming jargon, Yoda conditions is a programming style where the two parts of an expression are reversed from the typical order in a conditional statement. A Yoda condition places the constant portion of the expression on the left side of the conditional statement.

Swift is a high-level general-purpose, multi-paradigm, compiled programming language created by Chris Lattner in 2010 for Apple Inc. and maintained by the open-source community. Swift compiles to machine code and uses an LLVM-based compiler. Swift was first released in June 2014 and the Swift toolchain has shipped in Xcode since version 6, released in 2014.

In object-oriented programming, the safe navigation operator is a binary operator that returns null if its first argument is null; otherwise it performs a dereferencing operation as specified by the second argument.

ASP.NET Web Forms is a web application framework and one of several programming models supported by the Microsoft ASP.NET technology. Web Forms applications can be written in any programming language which supports the Common Language Runtime, such as C# or Visual Basic. The main building blocks of Web Forms pages are server controls, which are reusable components responsible for rendering HTML markup and responding to events. A technique called view state is used to persist the state of server controls between normally stateless HTTP requests.

References

  1. Dave Herman (14 December 2011). "Why coroutines won't work on the web". The Little Calculist. Archived from the original on 2016-03-06.
  2. "The Pyramid of Doom: A javaScript Style Trap". 27 November 2012. Archived from the original on 2015-12-09.
  3. Eberhardt, Colin (8 December 2014). "Tearing Down Swift's Optional Pyramid Of Doom". Archived from the original on 2016-07-31.
  4. "New Language Features in Visual Basic 14". 9 December 2014. Archived from the original on 2014-12-25.
  5. "Optional Chaining". Apple.
  6. "Null-conditional Operators (C# and Visual Basic)". Microsoft. 7 March 2024.
  7. "What's New for Visual C#". Microsoft. 21 May 2024.
  8. "What's New for Visual Basic". Microsoft. 21 February 2023.
  9. "Optional chaining in JavaScript".
  10. Joe Zimmerman (March 28, 2013). "What's The Point Of Promises?". telerik.com.