The Perils of Trying to be Clever

  • Narrator: Tyler, you are by far the most interesting single-serving friend I've ever met... see I have this thing: everything on a plane is single-serving...
  • Tyler: Oh I get it, it's very clever.
  • Narrator: Thank you.
  • Tyler: How's that working out for you?
  • Narrator: What?
  • Tyler: Being clever.
  • Narrator: Great.

While working on Vittles today, I had a facepalm moment, preceded by an hour of desperate confusion. The feeling will be familiar to many programmers--testing, debugging, head-scratching, more testing and debugging, then finally the moment of realization: "Duh!"

What went wrong? Well, as is the case with many software bugs, I tried too hard to be clever. I have some Recipes, and each Recipe has some Ingredients. Both Recipes and Ingredients keep track of their nutritional information in a related model called NutritionInfo. Whenever a Recipe changes (say, it gets another Ingredient), I want to recalculate the NutritionInfo; I do that by first recalculating all the NutritionInfo for the Ingredients.

I wanted to keep track of the success or failure of each Ingredient's recalculation (indicated by a True/False status). If all Ingredient recalculations were successful, I wanted to know about it. If any one of them failed, I wanted to know that too.

At first, I considered doing something like this:

    results = [ 
        ingredient.nutrition_info.recalculate()
        for ingredient in all_ingredients
    ]   
    success = all(results)

But to my cleverness-infested brain, this seemed like one step too many. Why not just write this?

    success = all(
        ingredient.nutrition_info.recalculate()
        for ingredient in all_ingredients
    )   

It seemed like a good idea at the time. But I soon discovered that many ingredients were mysteriously not getting their nutrition info recalculated. What could be wrong? Was some object not getting saved correctly? Was I missing some piece of data that would allow grams to be converted to cups? Finally it dawned on me--something that should have been obvious to a veteran Pythonista like myself--the all method short-circuits on the first occurrence of False, which means that as soon as any ingredient failed the recalculation, none of the ones after that would even be attempted.

After switching back to my initial, more-verbose-but-also-more-correct approach, it works as intended.

Moral: Cleverness doesn't always work out so well.