6 ms·
> If there’s an if condition inside a function, consider if it could be moved to the caller instead This idle conjecture is too rife with counterexamples. - I
by kazinator 1y ago
> If there’s an if condition inside a function, consider if it could be moved to the caller instead
This idle conjecture is too rife with counterexamples.
- If the function is called from 37 places, should they all repeat the if statement?
- What if the function is getaddrinfo, or EnterCriticalSection; do we push an if out to the users of the API?
I think that we can only think about this transformation for internal functions which are called from at most two places, and only if the decision is out of their scope of concern.
Another idea is to make the function perform only the if statement, which calls two other helper functions.
If the caller needs to write a loop where the decision is to be hoisted out of the loop, the caller can use the lower-level "decoded-condition helpers". Callers which would only have a single if, not in or around a loop, can use the convenience function which hides the if. But we have to keep in mind that we are doing this for optimization. Optimization often conflicts with good program organization! Maybe it is not good design for the caller to know about the condition; we only opened it up so that we could hoist the condition outside of the caller's loop.
These dilemmas show up in OOP, where the "if" decision that is in the callee is the method dispatch: selecting which method is called.
Techniques to get method dispatch out of loops can also go against the grain of the design. There are some patterns for
it.
E.g. wouldn't want to fill a canvas object with a raster image by looping over the image and calling canvas.putpixel(x, y, color). We'd have some method for blitting an image into a canvas (or a rectangular region thereof).
- deleted 1y ago[deleted]
- PaulRobinson 1y agoIf the function is called from 37 places, you need to refactor your code, but to answer your question on that point: it depends. DRY feels like the right answer, but I think we'd have to review an actual code example to decide. On examples where you're talking about a library function, I think you have to accept that as a library you're in a special place: you're on an ownership boundary. Data is moving across domains. You're moving across bounded contexts, in DDD-speak. So, no, you look after your own stuff. EnterCriticalSection suggests a code path where strong validation on entry - including if conditions - makes sense, and it should be thought of as a domain boundary. But when you're writing an application and your regular application functions have if statements, you can safely push them out. And within a library or a critical code section you can move the `if` up into the edges of it safely, and not down in the dregs. Manage your domain, don't make demands of other people's and within that domain move your control flow to the edge. Seems a reasonable piece of advice. However, as ever, idioms are only that, and need to be evaluated in the real world by people who know what they're doing and who can make sensible decisions about that context.
- worik 1y ago> If the function is called from 37 places, you need to refactor your code, Really? I do not have to think hard before I have a counter exampl: authentication I call authenticate() is some form from every API All 37 of them
- bognition 1y agoIf you are explicitly calling authenticate() for each api, you’re doing it “wrong”. At that point you want implied authentication not explicit authentication. Why not move it to some middleware that gets called in every api call?
- kazinator 1y agoBecause then you are calling middleware_caching_auth_broker() from 37 places instead of authenticate(). Just the name has changed, not the 37.
- all2 1y agoBut that's ok because the calls are hidden from the programmer. I'm not sure if my response is serious or tongue-in-cheek. Maybe a bit of both.
- KPGv2 1y ago> Because then you are calling middleware_caching_auth_broker() from 37 places No you aren't. You aren't really calling it from anywhere. The framework you're using, which you aren't writing, is calling the registered middleware. The topic here is complexity for the code structure because it's called from 37 different places. A registered middleware doesn't run into that issue because it doesn't get called anywhere that "code structure complexity" matters. Your reasoning is isomorphic to "I'm calling a bit shift millions of times because I have written some code in a programming language." Technically true but not what we're talking about here.
- 1y ago
- neoden 1y ago> If the function is called from 37 places, should they all repeat the if statement? the idea here is probably that in this case we might be able to split our function into two implementing true and false branches and then call them from 21 and 16 places respectively
- kazinator 1y agoThat's possible only if the condition is constant-foldable. You can achieve it by turning the if part into an inline function. Before: function(cond, arg) { if (cond) { true logic } else { false logic } } after: inline function(cond, arg) { cond ? function_true(arg) : function_false(arg) } Now you don't do anything to those 37 places. The function is inlined, and the conditional disappears due to cond being constant.
- panstromek 1y agoThe keyword here is `consider`. The article targets a somewhat specific design problem where this comes up, especially when you use tagged unions or something similar.