Live data from Hacker News

Push Ifs Up and Fors Down

matklad.github.io

191–200 of 209 posts

Re: Push Ifs Up and Fors Down

#191

Earlier quoted context omitted.

Pray tell, how many places is appropriate to call the same function? Is 5 too many? How about 6? When I hit 7, I have to refactor everything, right?

This only applies to a situation where you have a function that requires dynamic checks for preconditions. I would suggest that such a function (or how it's being used) is likely a blight already, but tolerable with very few call sites. In which case checking at the call site is the right move. And as you continue to abuse the function perhaps the code duplication will prompt you to reconsider what you are doing.

So if a function dereferences a pointer, it doesn't make sense to check that it's not null inside the function?

Unless there's an actual performance implication, this is all purely a matter of taste. This is the kind of broadly true, narrowly false stuff that causes people to destroy codebases. "I can't write it this way, because I have to push ifs up and fors down!!!" It's a totally fake requirement, and it imposes a fake constraint on the design.

Re: Push Ifs Up and Fors Down

#192
post #138
post #134

Earlier quoted context omitted.

And then at some point someone shows you how Classes can be verbs, and functions can be nouns, and your brain hurts for a while. You overuse that paradigm for a while, and eventually learn to find the appropriate balance of ideas.

Haven’t seen that yet after 25 years. It just always seems like lazy naming when this isn’t followed. Maybe I missed something.

Agreed. Compelling receipts required.

Re: Push Ifs Up and Fors Down

#193
post #170

> 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 interna…

> 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

That'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.

Re: Push Ifs Up and Fors Down

#194

Earlier quoted context omitted.

This only applies to a situation where you have a function that requires dynamic checks for preconditions. I would suggest that such a function (or how it's being used) is likely a blight already, but tolerable with very few call sites. In which case checking at the call site is the right move. And as you continue to abuse the function perhaps the code duplication will prompt you to reconsider what you are doing.

So if a function dereferences a pointer, it doesn't make sense to check that it's not null inside the function? Unless there's an actual performance implication, this is all purely a matter of taste. This is the kind of broadly true, narrowly false stuff that causes people to destroy codebases. "I can't write it this way, because I have to push ifs up and fors down!!!" It's a totally fake requirement, and it imposes…

IMO you should assert it's not null. There should never be a circumstance where you pass a null pointer to a function.

Re: Push Ifs Up and Fors Down

#195

Earlier quoted context omitted.

This only applies to a situation where you have a function that requires dynamic checks for preconditions. I would suggest that such a function (or how it's being used) is likely a blight already, but tolerable with very few call sites. In which case checking at the call site is the right move. And as you continue to abuse the function perhaps the code duplication will prompt you to reconsider what you are doing.

So if a function dereferences a pointer, it doesn't make sense to check that it's not null inside the function? Unless there's an actual performance implication, this is all purely a matter of taste. This is the kind of broadly true, narrowly false stuff that causes people to destroy codebases. "I can't write it this way, because I have to push ifs up and fors down!!!" It's a totally fake requirement, and it imposes…

If there is a performance implication of moving the if into the callers or not, you can do it with an inline function.

  static inline int function(blob *ptr, int arg)
  {
     if (ptr == NULL)
       return ERR_NULL;
     return real_function(ptr, arg);
  }
Just like that, we effectively moved the if statement into 37 callers, where the compiler may be smart enough to hoist it out of a for loop when it sees that the pointer is never changed in the loop body, or to eliminate it entirely when it sees that the pointer cannot be null.

Re: Push Ifs Up and Fors Down

#196

Earlier quoted context omitted.

So if a function dereferences a pointer, it doesn't make sense to check that it's not null inside the function? Unless there's an actual performance implication, this is all purely a matter of taste. This is the kind of broadly true, narrowly false stuff that causes people to destroy codebases. "I can't write it this way, because I have to push ifs up and fors down!!!" It's a totally fake requirement, and it imposes…

IMO you should assert it's not null. There should never be a circumstance where you pass a null pointer to a function.

ISO C allows:

  free(NULL); // convenient no-op, does nothing

  fflush(NULL); // flush all streams; done implicitly on normal exit

  time(NULL); // don't store time_t into a location, just return it

  strtol(text, NULL, 10);  // not interested in pointer to first garbage char

  setbuf(stream, NULL);  // allocate a buffer for stream

  realloc(NULL, size); // behave like malloc(size)
and others. More examples in POSIX and other APIs:

  sigprocmask(SIG_UNBLOCK, these_sigs, NULL); // not interested in previous signal mask

  CreateEventA(NULL, FALSE, FALSE, NULL); // no security attributes, no name
Reality just ran over your opinion, oops!

Re: Push Ifs Up and Fors Down

#197
post #69

Earlier quoted context omitted.

You can express a lot of concepts just through types in languages with richer type systems.

Even without a rich type system you can express a lot of things just through naming. You just can't enforce those assumptions.

You can enforce them (statically) by other means if you’re determined enough, eg by using lint rules which enforce type-like semantics which the type system itself doesn’t express.

Re: Push Ifs Up and Fors Down

#198

Earlier quoted context omitted.

Even without a rich type system you can express a lot of things just through naming. You just can't enforce those assumptions.

You can enforce them (statically) by other means if you’re determined enough, eg by using lint rules which enforce type-like semantics which the type system itself doesn’t express.

This does rely on the language having a sophisticated-enough type system to be able to extract enough type information for the rules to work in the first place.

Re: Push Ifs Up and Fors Down

#199
post #160

Earlier quoted context omitted.

My mental model: align with the world the very specific code I'm writing lives in. From domain specifics, to existing patterns in the codebase, to the stage in the data pipeline I'm at, performance profile, etc. I used to try and form these kinds of rules and heuristics for code constructs, but eventually accepted they're at the wrong level of abstraction to be worth keeping around once you write enough code. It's te…

I feel this kind of critique, which I see often as a response to articles like this, is so easy as to be meaningless. How is one supposed to ever talk about general principles without using simplified examples? Aren't you just saying "Real code is more complicated than your toy example"? Well sure, trivially so. But that's by design. > Perfect example is the "redundancies and dead conditions" mentioned: we're making…

Well I guess some comments need to be considered in totality, rather contextomies that enforce whatever point you're trying to make :)

I spelled out the problem pretty clearly.

> I used to try and form these kinds of rules and heuristics for code constructs, but eventually accepted they're at the wrong level of abstraction to be worth keeping around once you write enough code.

It's the wrong level of abstraction to form (useful) principles at, and the example chosen is just a symptom of that.

I'm not sure why we're acting like I said the core problem with this article is that it uses simple examples.

Re: Push Ifs Up and Fors Down

#200

Earlier quoted context omitted.

IMO you should assert it's not null. There should never be a circumstance where you pass a null pointer to a function.

ISO C allows: free(NULL); // convenient no-op, does nothing fflush(NULL); // flush all streams; done implicitly on normal exit time(NULL); // don't store time_t into a location, just return it strtol(text, NULL, 10); // not interested in pointer to first garbage char setbuf(stream, NULL); // allocate a buffer for stream realloc(NULL, size); // behave like malloc(size) and others. More examples in POSIX and other APIs…

I'm sorry. Are you claiming the people who designed those functions made good choices? They altered the behavior of the function considerably for a single input value that is more likely to be a bug than not.
Post reply on HN