How to write functions?
The first rule of functions is that they should be small. The second rule of functions is that they should be smaller than that.
The Stepdown Rule
We want the code to read like a top-down narrative. We want every function to be followed by those at the next level of abstraction so that we can read the program, descending one level of abstraction at a time as we read down the list of functions
Function Arguments
The ideal number of arguments for a function:
0 (niladic)
1 (monadic)
2 (dyadic)
3 (triadic) should be avoided where possible.
More than three (polyadic) requires very special justification and then shouldn’t be used anyway.
Arguments are even harder from a testing point of view. Imagine the difficulty of writing all the test cases to ensure that all the various combinations of arguments work properly. If there are no arguments, this is trivial. If there’s one argument, it’s not too hard. With two arguments the problem gets a bit more challenging. With more than two arguments, testing every combination of appropriate values can be daunting.
One input argument is the next best thing to no arguments. 𝗿𝗲𝗻𝗱𝗲𝗿𝗣𝗮𝗴𝗲(𝗽𝗮𝗴𝗲𝗗𝗮𝘁𝗮) is pretty easy to understand. Clearly we are going to render the data in the pageData object.
Query Separation
Functions should either do something or answer something, but not both. Either your function should change the state of an object, or it should return some information about that object. Doing both often leads to confusion.
Consider, for example, the following function:
function set(attribute: string, value: string){}
This function sets the value of a named attribute and returns true
if it is successful and false
if no such attribute exists. This leads to odd statements like this:
if (set("username", "j471n"))...
Imagine this from the point of view of the reader. What does it mean? Is it asking whether the “username” attribute was previously set to "j471n:? Or is it asking whether the "username" attribute was successfully set to “j471n”?
The real solution is to separate the command from the query so that the ambiguity cannot occur.
if (attributeExists("username")) {
setAttribute("username", "j471n"){}
}
Error Handling
Functions should do one thing. Error handing is one thing. Thus, a function that handles errors should do nothing else. that This implies if the keyword tr
y exists in a function, it should be the very first word and that there should be nothing after the catch/finally
blocks.