3 ms·
Another perspective in defense of long functions is that they enable you to spot common expressions/statements within the body, for example: void long_func
by bluetomcat 10y ago
Another perspective in defense of long functions is that they enable you to spot common expressions/statements within the body, for example:
void long_func(void) {
...
if (player.alive && player.health == 100) {
....
}
...
if (some_other_condition && player.alive && player.health == 100) {
}
}
Conventional wisdom says that you should write a function `is_player_untouched` and substitute the composite expressions with function calls, but the code in question can be refactored in a much more straightforward way:
void long_func(void) {
...
const bool player_untouched = is_player_untouched();
if (player_untouched) {
....
}
...
if (some_other_condition && player_untouched) {
}
}
Had the function body been split into more functions for "clarity", you would be doing duplicate calls to `is_player_untouched()` which go unnoticed because they would be buried deep in the call graph.
- rubber_duck 10y agoThis leads to brittle code - it's easy to determine if is_player_untouched state changes between those two conditions when writing the code but when someone else edits that code it's also easy to introduce something that will break it and the monolithic/big function makes it hard to keep track of assumptions like this and it leads to a lot of code changes in those big functions as well. Even worse this kinds of bugs usually end up being hard to test if you're not covered with unit tests, and games rarely are, and big functions go against testing practices.
- bluetomcat 10y agoIt sure leads to brittle code if assumptions and contracts in the code are not crystal clear, but Carmack's point in this particular article was about writing "consistently performing" code which doesn't degrade under specific conditions. In my refactored example, you wouldn't be eventually calling `is_player_untouched()` once more if `some_other_condition` is true.
- icebraining 10y agoHopefully, that should be something the compiler could do on its own; assuming the language has appropriate semantics, it could identify the common expression, prove that it's value can't have changed in between, and then do the substitution on its own. Is there any language where this is implemented, or is the effort too great for the gains?
- accatyyc 10y agoThis would maybe be possible in C if the "functional" keyword was added (as suggested in the letter), and the compiler can be sure that the function doesn't use anything apart from its (const) arguments to generate a return value. Then it also would need to make sure that those parameters can't change between different calls within the same function. This wouldn't be feasible in the example above, since that function explicitly depends on outside variables. If you were to supply both `health` and `alive` as arguments, you could pretty much write the check yourself from the beginning. Of course, there are many bigger checks that could benefit from this, but still, you (or the compiler) have to make sure that all functions are pure, that no arguments can change etc etc. I imagine that this could make compile times quite slow (and also complicated to write the compiler itself).
- deleted 10y ago[deleted]
- Jaruzel 10y agoI have nothing to add to this, other than... "OK this guy has clearly worked on the Quake source code..." :)