9/26/2009

09-26-09 - Habits - Code Inlining

There's been an off and on discussion with some peers in email about the issue of putting small code functionality directly inline in a bigger function vs. splitting it out. I don't think it's actually an interesting topic so I don't want to repeat it here, but it made me think of something I want to write about.

Basically the contention is that even if you have a logically separate piece of work, sometimes it's better to go ahead and write it inline in the larger function, because it makes the code flow more linear and imperative and thus easier to read and debug. There are also obvious disadvantages to writing code inline - less strong separation of functional bits, temptation to copy-paste code, less ability to debug and test functions independently, etc. etc. All the pros and cons are obvious and thus not interesting.

The key point to me is the issue that while code inline may be a win sometimes - it's a big loss at other times, and it requires you to make a careful smart logical decision about which way to go.

That's very bad. I think most smart people as they get older come to the realization that you shouldn't put yourself in positions where you have to make careful logical decisions over and over. Instead you should craft habits and rules for yourself that help you do the right thing without having to make careful decisions.

One obvious case that most smart people figure out is diet and exercise, or booze or other indulgences. Yes, it's perfectly fine to have a some dessert once in a while, but if you open that door for yourself you're putting yourself in a situation where you are consciously making a decision "is it okay for me to have cake today?" and you will inevitably get lazy and make the wrong choice sometimes.

As usual the best analogy is poker, and it's how this point was really made real for me. Smart people often start out playing poker trying to logically reason out every single action and think they don't need to be constrained by simple rules or habits. That's great if you really are correctly thinking through every situation, but you inevitably get tired or lazy or make mistakes, and if you're not constraining yourself with rules, you can make huge mistakes.

For example, there might be cases where the best play is to limp aces up front, or to not reraise with aces, but correctly making that decision requires a lot of careful thought, and the upside to making that decision is pretty small, while the downside to doing it in the wrong situation is very big. It's best to just make a rule for yourself that you always raise or reraise. It simplifies things, it removes decision points and lets you focus on more important issues. It might be +EV to call raises sometimes with hands like 68o, but it's best to just give yourself a rule that you never do that.

To be clear - these rules are specifically non-optimal. By making rules for yourself you are intentionally choosing to not try to be 100% optimal, so of course someone can say "you could have done something better there". That's not the point. The point is that if you try to make perfect decisions all the time you will occasionally fail very badly.

Winning poker play (or good coding, or good life living) are largely about making it easy for yourself to do the right thing.

5 comments:

  said...

my personal favorite is the local function. Keeps code flow reasonable, but doesn't duplicate code everywhere all the time.

void foo() {
struct local_t {
static void bar() {
}
};
local_t::bar();
local_t::bar();
local_t::bar();
}

its exceptionally convenient when you want to qsort or std::sort. No more hunting for the correct sort function, just put it inline.

cbloom said...

Yeah local functions are pretty gross in C++99, but they will be neat in C++0x.

cbloom said...

Actually I really don't like the local function. It's basically the worst of both worlds. It makes the code flow nonlinear, and it also fails to make a robust utility function that's easily shared and reused. The fact that you can't use it in templates makes it basically useless in C++9x.

Also it's another conscious choice which means another possibility for mistakes. If there's a valid complaint about C++ it's that it gives the practitioner too many difficult choices in how to write code. Most modern variants like Java and C# improve C++ by greatly reducing the ways you can do things.

  said...

yeah, I'm really looking forward to lambda functions in the new standard. local functions make some things nice, but the certainly have their drawbacks. One large one is that they are hard to debug as the debugger messes up quite frequently in them. In general I'm a big fan of linear code. None of that function fan-in fan-out stuff just because. If it makes sense to do it that way, then do it that way. Otherwise keep it simple.

  said...

btw, the linear code argument is not a new one. Its been around since the inception of high level programming languages. I can't find the article at the moment, but there have been studies done, and people aren't very good at understanding code that isn't linear. This is taken to extremes in other industries (like the aviation industry) where people's lives are on the line. They don't like to have any branches in their code flow at all if they can help it. One bug after all can cost them huge amounts in court, as well as its a stain on their conscious. However, this leads people to places they don't like to be.
Linear code -> inlining code -> less code reuse -> why are we using c++?
People are sometimes a little over passionate when it comes to their language and coding style of choice. I try to have no opinions myself and keep an open mind. Afterall, in some places I'm sure C++ makes excellent use, however in others it probably hurts more than it helps. All to often I find people using virtual functions for example when there is absolutely no need for it. Just because they might at some point in time in a mythical future. Maybe I should write a blog entry. This comment is getting a little long.

old rants