Live data from Hacker News

Push Ifs Up and Fors Down

matklad.github.io

51–60 of 209 posts

Re: Push Ifs Up and Fors Down

#51

Terrible advice. It's the exact opposite of "Tell, don't ask". Performance of an if-statement and for-loop are negligent. That's not the bottleneck of your app. If you're building something that needs to be highly performant, sure. But that's not the majority. https://martinfowler.com/bliki/TellDontAsk.html

He’s giving object oriented design the medal for cache locality’s victory.

Re: Push Ifs Up and Fors Down

#52
post #4

The author's main concern seems to be optimising performance critical code.

Hmmm. Seems like he’s optimizing clarity of thought, first. The performance gain just comes along for the ride. If I were to summarize the article, I’d say that it’s advocating a pattern where you write code where higher layers decide what needs to be done and then lower layers do it, using a combination of straight line code and simple loops, with little to no further conditionality. Obviously, that represents an ideal.

Re: Push Ifs Up and Fors Down

#53
My weird mental model: You have a tree of possible states/program flow. Conditions prune the tree. Prune the tree as early as possible so that you have to do work on fewer branches.

Don’t meticulously evaluate and potentially prune every single branch, only to find you have to prune the whole limb anyways.

Or even weirder: conditionals are about figuring out what work doesn’t need to be done. Loops are the “work.”

Ultimately I want my functions to be about one thing: walking the program tree or doing work.

Re: Push Ifs Up and Fors Down

#54
The performance gap of running a for loop inside or outside a function call is negligible in most real usage.

The premise that you can define best patterns like this, removed from context with toy words like frobnicate, is flawed. You should abstract your code in such a way that the operations contained are clearly intuited by the names and parameters of the abstraction boundaries. Managing cognitive load >>> nickle and dime-ing performance in most cases.

Re: Push Ifs Up and Fors Down

#55
post #24

Terrible advice. It's the exact opposite of "Tell, don't ask". Performance of an if-statement and for-loop are negligent. That's not the bottleneck of your app. If you're building something that needs to be highly performant, sure. But that's not the majority. https://martinfowler.com/bliki/TellDontAsk.html

Performance of any given CPU instruction is negligible, yet somehow they accumulate to noticeable values.

Amen.

Re: Push Ifs Up and Fors Down

#56
post #35

It’s a bit niche for HN, but SwiftUI rendering works way better when following this. In a ForEach, you really shouldn’t have any branching, or you‘ll pay quite catastrophic performance penalties. I found out the hard way when rendering a massive chart with Swift Charts. All branching must be pushed upwards.

Why? Does it interpret the code?

Re: Push Ifs Up and Fors Down

#57

Earlier quoted context omitted.

Doesn't this obfuscate what assumptions you can make when trying to understand the core logic? You prefer to examine all the call chains everywhere?

This is why we invented type systems. No need to examine call chains, just examine input types. The types will not only tell you what assumptions you can make, but the compiler will even tell you if you make an invalid assumption!

You can't shove every single assumption into the type system...

Re: Push Ifs Up and Fors Down

#58
post #43

Sometimes I like to put the conditional logic in the callee because it prevents the caller from doing things in the wrong order by accident. Like for example, if you want to make an idempotent operation, you might first check if the thing has been done already and if not, then do it. If you push that conditional out to the caller, now every caller of your function has to individually make sure they call it in the rig…

You’ve kind of answered your own question here.

> If you push that conditional out to the caller, now every caller of your function has to individually make sure they call it in the right way to get a guarantee of idempotency

In this situation your function is no longer idempotent, so you obviously can’t provide the guarantee. But quite frankly, if you’re having to resort to making individual functions implement state management to provide idempotency, then I suspect you’re doing something very odd, and have way too much logic happening inside a single function.

Idempotent code tends to fall into two distinct camps:

1. Code that’s inherently idempotent because the data model and operations being performed are inherently idempotent. I.e. your either performing stateless operations, or you’re performing “PUT” style operations where in the input data contains all the state the needs to be written.

2. Code that’s performing more complex business operations where you’re creating an idempotent abstraction by either performing rollbacks, or providing some kind of atomic apply abstraction that ensures partial failures don’t result in corrupted state.

For point 1, you shouldn’t be checking for order of operations, because it doesn’t matter. Everything is inherently idempotent, just perform the operations again.

For point 2, there is no simple abstraction you can apply. You need to have something record the desired operation, then ensure it either completes or fails. And once that happens, ensures that completion or failure is persistent permanently. But that kind of logic is not the kind of thing you put into a function and compose with other operations.

Re: Push Ifs Up and Fors Down

#59
post #43

Sometimes I like to put the conditional logic in the callee because it prevents the caller from doing things in the wrong order by accident. Like for example, if you want to make an idempotent operation, you might first check if the thing has been done already and if not, then do it. If you push that conditional out to the caller, now every caller of your function has to individually make sure they call it in the rig…

Maybe write the functions without the checks, then have wrapper functions that just do the checks and then call the internal function?

Re: Push Ifs Up and Fors Down

#60
post #34
post #33

Earlier quoted context omitted.

I agree with you. Cyclomatic complexity check may be my least favorite of these rules. I think any senior developer almost always “knows better” than the tool does what is a function of perfectly fine complexity vs too much. But I have to grudgingly grant that they have some use since if the devs in question routinely churn out 100-line functions that do 1,000 things, the CCC will basically coincidentally trigger and…

Cyclomatic complexity may be a helpful warning to detect really big functions, but the people who worry about cyclomatic complexity also seem to be the sort of people who want to set the limit really low and get fiesty if a function has much more than a for loop with a single if clause in it. These settings produce those code bases where no function anywhere actually does anything, it just dispatches to three other f…

I call this "poltergeist code". Dozens of tiny functions that together clearly does something complex correctly, but it's very hard to find where and how it's actually done.
Post reply on HN