The Pragmatic Defense
04 Apr 2012, 16:57
Michael Krumlauf (1 post)
This was a profitable article. I do want to point out that not all functions/methods can return a boolean value (what if a value is to be computed and returned?) The article would be even more profitable if such cases were considered and examples given.
Now that I think of it, C++ and other non-garbage collected languages might be more suited to using the boolean return value idiom frequently by pre-allocating a mutable “result” object and passing it in as a parameter to the function, with the function providing the desired value in the state of the result object. The boolean return value idiom would be of lesser utility when programming in garbage collected languages.
All that being said, I appreciate the high quality and usefulness of PragPub articles. Thanks!
06 Apr 2012, 05:39
James Bonang (2 posts)
Thank you very much for commenting on the article.
You’re correct; in many circumstances it is not practical or possible to have a method return a Boolean value and I should have pointed this out in the article. A constructor cannot return a value, for example, and it is unreasonable for a simple member accessor method to do so. The Pragmatic Defense addresses these cases differently; the Word Count example application referenced in the article provides illustrations (but another article would be needed to cover this topic).
In the case of methods that compute a value, returning a bool has many advantages, even for simple computations. Here’s an example that might be what you are looking for.
Let’s consider a commonly used C function to see how returning a Boolean, and applying the Pragmatic Defense, will help you isolate problems.
The sqrt function in the C header file math.h (or cmath) computes and returns the principal square root of its argument; here’s the function signature:
There is an implicit constraint on the value of x - it must be a non-negative real number (that can be represented as a double precision floating point value). It is not valid to call sqrt with an argument that is a negative real number or a value such as NaN (a special bit pattern indicating “Not a Number”). The principle square root cannot be computed for such values.
Let’s encapsulate the sqrt call within a C++ function that returns a Boolean; here’s the function signature:
A Boolean value is returned indicating success or failure. The computed principal square root is returned in the dPrincipalSquareRoot argument (a reference to a double). Within computePrincipalSquareRoot, we can check preconditions such as whether the input argument dNonNegativeRealNumber is indeed non-negative. We can also check if it is set to NaN (and other special, but invalid, values).
Using the Pragmatic Defense, the body of the computePrincipalSquareRoot method is outlined as shown below:
You could also check postconditions within computePrincipalSquareRoot as well. (For instance, you could check that the square of the computed principal square root is within some epsilon of the value of the input argument dNonNegativeRealNumber.)
If invalid data were passed in to computePrincipalSquareRoot, the Boolean return value would let you know that the computed value, dPrincipalSquareRoot, should not be used. You would avoid using the value in subsequent computations. (If the value were propagated, the error would become much more difficult to track down.) Moreover, if the input argument did not satisfy one of the preconditions, you would know that the failure occurred in the code leading up to the call to computePrincipalSquareRoot. The error message emitted to the log would help you characterize the failure, giving you clues as to where to look for the fault.
Incidentally, testing special cases such as NaN is tricky. The following approach may help:
You can then pass dValue in to computePrincipalSquareRoot in your unit tests.
(Also, note that sqrt is overloaded in C++ header file “complex” - careful, this example is using the version in math.h.)
In summary, methods intended to compute a value can return a Boolean indicating success or failure; the computed value may be returned as an argument passed by reference. A Boolean return value may not be suitable in all circumstances, but in many cases, using the Pragmatic Defense - including Boolean return values - will help you track down faults quickly.
Please let me know if the above example is what you are looking for. I’m glad you found the article profitable and I very much appreciate your comments. Thanks once again.
04 May 2012, 22:08
David Birch (2 posts)
I enjoyed this article so much that it lead me to the forum!
I am learning to program in ruby and think that return parameters are counterintuitive. I think that arguments are most naturally inputs to a function.
An author of another book - “clean code”, by another publisher - makes a point in the chapter on functions that I thought was pertinent. When object oriented programming, functions that must change the state of something aught to change the state of their object (this).
This approach would allow for boolean return values as well as avoid “results” parameters.
Do you have any thought on this?
08 May 2012, 16:46
James Bonang (2 posts)
Thank you for commenting on the article and sharing your thoughts on output arguments.
You’re correct that for object-oriented languages arguments are most naturally inputs to a function. Robert Martin reinforces this in Clean Code:
”…much of the need for output arguments disappears in OO languages because this is intended to act as an output argument” [Martin, Robert C. Clean Code: A Handbook of Agile Software Craftsmanship. Pearson Education (USA), 2008, p. 45]
Certainly, input arguments appear far more frequently than do output arguments, even in non-object-oriented languages like C.
Mr. Martin also points out that “If your function must change the state of something, have it change the state of its owning object.” As you suggested, this allows the method return value to be utilized to indicate success or failure. The example method “takeNote” (bool CNotes::takeNote(std::string & strNote)) from the article conforms to this idiom; it accepts a single input argument (a note string), changes the state of its object (adds the note string to a collection of notes), and returns a Boolean value, indicating success or failure.
In some (comparatively rare) cases, output arguments come in handy. For example, the computePrincipalSquareRoot function in the previous posting, acts on a primitive type (a double precision floating point value in this case) and essentially wraps a C standard library function (sqrt from math.h); it is not object-oriented (and this may be why it seems counterintuitive). Use of Design by Contract with the square root function is discussed in the Pragmatic Programmer [Hunt Andrew, and David Thomas. The Pragmatic Programmer: from Journeyman to Master. Boston: Addison Wesley, 2000, p. 115]
Interestingly, Ruby offers another option. The return statement in Ruby allows you to return multiple values [Thomas, Dave, Chad Fowler and Andy Hunt. Programming Ruby: the Pragmatic Programmer’s Guide, 2nd ed. The Pragmatic Programmer’s LLC, 2005, p. 83.]
Please let me know if the above is what you’re looking for. Thanks again for your comments on the article and for sharing your insights.
|You must be logged in to comment|