Pyramid of doom (programming): Difference between revisions

Content deleted Content added
m If windows("Main") is null, .views() will be called on null--better just to be general I expect :3
m common typo "programer" corrected
 
(63 intermediate revisions by 43 users not shown)
Line 1:
{{short description|Computer programming problem}}
{{confuse|Pyramid of Doom}}
{{sources exist|date=January 2024}}
In [[computer programming]], the '''pyramid of doom''' is a common problem that arises when a program uses many levels of nested indentation to control access to a function. It is commonly seen when checking for [[null pointer]]s or handling [[Callback (computer programming)|callbacks]].<ref name="Herman, 2011">{{Cite web|title=Why coroutines won’t work on the web|author=Dave Herman|date=14 December 2011 |website=The Little Calculist|url=http://calculist.org/blog/2011/12/14/why-coroutines-wont-work-on-the-web/}}</ref> Two examples of the term are used to a particular programming style in [[JavaScript]],<ref>{{cite web |url=http://tritarget.org/blog/2012/11/28/the-pyramid-of-doom-a-javascript-style-trap/ |title=The Pyramid of Doom: A javaScript Style Trap |date=27 November 2012}}</ref> and the nesting of [[Conditional (computer programming)|if statements]] that occurs in [[object-oriented programming]] languages when one of the objects may be a null pointer.<ref>{{cite web |url=http://blog.scottlogic.com/2014/12/08/swift-optional-pyramids-of-doom.html |title=Tearing Down Swift's Optional Pyramid Of Doom |first=Colin |last=Eberhardt |date=8 December 2014}}</ref><ref>{{cite web |url=http://blogs.msdn.com/b/vbteam/archive/2014/12/09/new-language-features-in-visual-basic-14.aspx |title=New Language Features in Visual Basic 14 |date=9 December 2014}}</ref>
 
In [[computer programming]], a common challenge facing [[Systems programming|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 data to be written is available; 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.
 
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 statement|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 (programming)|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 (typesetting)|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'''.
 
InFor [[computer programming]]example, the '''pyramid of doom''' is a common problem that arises when a program uses many levels of nested indentation to control access to a function. It is commonly seen when checking for [[null pointer]]s or handling [[Callback (computer programming)|callbacks]].<ref name="Herman, 2011">{{Cite web|title=Why coroutines won’twon't work on the web |author=Dave Herman |date=14 December 2011 |website=The Little Calculist |url=http://calculist.org/blog/2011/12/14/why-coroutines-wont-work-on-the-web/ |archiveurl=https://web.archive.org/web/20160306010725/http://calculist.org/blog/2011/12/14/why-coroutines-wont-work-on-the-web/ |archivedate=2016-03-06 |url-status=live}}</ref> Two examples of the term are usedrelated to a particular programming style in early versions of [[JavaScript]],<ref>{{cite web |url=http://tritarget.org/blog/2012/11/28/the-pyramid-of-doom-a-javascript-style-trap/ |title=The Pyramid of Doom: A javaScript Style Trap |date=27 November 2012 |archiveurl=https://web.archive.org/web/20151209151711/http://tritarget.org/blog/2012/11/28/the-pyramid-of-doom-a-javascript-style-trap |archivedate=2015-12-09}}</ref> and the nesting of [[Conditional (computer programming)|if statements]] that occurs in [[object-oriented programming]] languages when one of the objects may be a null pointer.<ref>{{cite web |url=http://blog.scottlogic.com/2014/12/08/swift-optional-pyramids-of-doom.html |title=Tearing Down Swift's Optional Pyramid Of Doom |first=Colin |last=Eberhardt |date=8 December 2014 |archiveurl=https://web.archive.org/web/20160731194838/http://blog.scottlogic.com/2014/12/08/swift-optional-pyramids-of-doom.html |archivedate=2016-07-31 |url-status=live}}</ref><ref>{{cite web |url=http://blogs.msdn.com/b/vbteam/archive/2014/12/09/new-language-features-in-visual-basic-14.aspx |title=New Language Features in Visual Basic 14 |date=9 December 2014 |archiveurl=https://web.archive.org/web/20141225220053/http://blogs.msdn.com/b/vbteam/archive/2014/12/09/new-language-features-in-visual-basic-14.aspx |archivedate=2014-12-25 |url-status=live}}</ref>
 
==Examples==
{{how-to|section|date=June 2020}}
 
Most modern [[object-oriented programming language]]s use a notationcoding 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:
===Object property dereferencing===
Most modern [[object-oriented programming language]]s use a notation 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:
 
<sourcesyntaxhighlight lang=javascript >
theWidth = windows("Main").views(5).size().width();
</syntaxhighlight>
</source>
 
This code contains four different instructions; it first looks in the arraycollection of windows for a window with the name "Main", getsthen looks in that window's views collection for the 5th subview within it, then calls the <code>size</code> method to return a structure with the view's dimensions, and finally calls the <code>width</code> method on that structure to produce a result that is assigned to a variable name <code>theWidth</code>.
 
The problem with this approach is that the code assumes that all of these values exist. While it is reasonable to suggestexpect 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 possibilitiesassumptions areis truewrong, onethe ofcorresponding the methodscall will be invoked on null,result producingin 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:
<sourcesyntaxhighlight lang=javascript >
if windows.contains("Main") {
if windows("Main").views.contains(5) {
theWidth = windows("Main").views(5).size().width();
//more code that works with theWidth
}
}
}
</source>
</syntaxhighlight>
 
If the programmer wishes to use that value based on whether or not it exists and is valid, the functional code inside the <code>if</code> statements is all pushed to the right, making it difficult to read longer lines. This often leads to attempts to "flatten" the code:
<sourcesyntaxhighlight lang=javascript >
if windows.contains("Main") { theWindow = windows("Main") }
if theWindow != null && theWindow.views.contains(5) { theView = theWindow.views(5) }
if theView != null {
theWidth = theView.size().width();
//additional code
}
}
</syntaxhighlight>
</source>
 
Or alternatively:
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 (programming language)|Swift]] added the concept of [[Safe navigation operator|optional chaining]] in if statements<ref>{{cite web |url=https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/OptionalChaining.html |title=Optional Chaining |website=Apple}}</ref> while Microsoft's [[C Sharp (programming language)|C#]] 6.0 and [[Visual Basic .NET|Visual Basic]] 14 added the [[Safe navigation operator|null-conditional]] operators <code>?.</code> and <code>?[</code> for member access and indexing, respectively.<ref>{{cite web |url=https://msdn.microsoft.com/en-us/library/Dn986595.aspx |title=Null-conditional Operators (C# and Visual Basic) |website=Microsoft}}</ref><ref>{{cite web |url=https://msdn.microsoft.com/en-us/library/hh156499.aspx |title=What's New for Visual C#|website=Microsoft}}</ref><ref>{{cite web | url=https://msdn.microsoft.com/en-us/library/we86c8x2.aspx|title=What's New for Visual Basic|website=Microsoft}}</ref> 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:
<sourcesyntaxhighlight lang="javascript ">
if theWidth = !windows.contains("Main")?.views(5)?.size.width; {
// handle error
</source>
} else if !windows("Main").views.contains(5) {
// handle error
} else {
theWidth = windows("Main").views(5).size().width();
//more code that works with theWidth
}
 
</syntaxhighlight>
 
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 (programming language)|Swift]] added the concept of [[Safe navigation operator|optional chaining]] in if statements<ref>{{cite web |url=https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/OptionalChaining.html |title=Optional Chaining |website=Apple}}</ref> while Microsoft's [[C Sharp (programming language)|C#]] 6.0 and [[Visual Basic .NET|Visual Basic]] 14 added the [[Safe navigation operator|null-conditional]] operators <code>?.</code> and <code>?[</code> for member access and indexing, respectively.<ref>{{cite web |url=https://msdn.microsoft.com/en-us/library/Dn986595.aspx |title=Null-conditional Operators (C# and Visual Basic) |website=Microsoft|date=7 March 2024 }}</ref><ref>{{cite web |url=https://msdn.microsoft.com/en-us/library/hh156499.aspx |title=What's New for Visual C#|website=Microsoft|date=21 May 2024 }}</ref><ref>{{cite web | url=https://msdn.microsoft.com/en-us/library/we86c8x2.aspx|title=What's New for Visual Basic|website=Microsoft|date=21 February 2023 }}</ref> [[JavaScript]] added support for the optional chaining operator in 2020.<ref>{{cite web | url=https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining|title=Optional chaining in JavaScript|date=28 October 2024 }}</ref> 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:
<syntaxhighlight lang=javascript >
theWidth = windows("Main")?.views(5)?.size.width;
</syntaxhighlight>
would assign null to <code>theWidth</code> 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 <code>if let</code> statement, also known as "optional binding":
<sourcesyntaxhighlight lang=javascript >
if let theView = windows("Main")?.views(5) {
//do things knowing the view exists...
theWidth = theView.size.width
}
}
</syntaxhighlight>
</source>
 
==Resolution==
{{Original research|Resolution|reason=This section reads as if it may be influenced by "Clean Code" but does not cite any references. The book Clean Code is under considerable scrutiny nowadays for the practicality (and even validity) of the recommendations given.|date=December 2024}}
 
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:
<syntaxhighlight lang=javascript >
main() {
aaaaa() {
bbbbb() {
ccccc() {
ddddd() {
// do something now
}
}
}
}
}
</syntaxhighlight>
 
You can break up the functionality like this:
 
<syntaxhighlight lang=javascript >
doSomething() {
// do something now
}
 
CC() {
ccccc() {
ddddd() {
doSomething()
}
}
}
 
main() {
aaaaa() {
bbbbb() {
CC()
}
}
}
</syntaxhighlight>
 
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 for a programmer to write without erring, easier to read, and easier to verify.
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==
{{wiktionary|pyramid of doom}}
* [[Futures and promises|Promises]], a technique for avoiding the pyramid of doom, e.g. used in JavaScript<ref>{{cite web|title=What's The Point Of Promises?|date=March 28, 2013|author=Joe Zimmerman|website=telerik.com|url=http://www.telerik.com/blogs/what-is-the-point-of-promises}}</ref>
*[[Law of Demeter]]
* [[Safe navigation operator]], a programming language operator that lets one avoid the pyramid of doom
 
==References==
{{reflist|30em}}
 
[[Category:Programming constructs]]