The older I get, the more my code (mostly C++ and Python) has been moving towards mostly-functional, mostly-single static assignment (let assignments).<p>Lately, I've noticed a pattern emerging that I think John is referring to in the second part. The situation is that often a large function will be composed of many smaller, clearly separable steps that involve temporary, intermediate results. These are clear candidates to be broken out into smaller functions. But, a conflict arises from the fact that they would each only be invoked at exactly one location. So, moving the tiny bits of code away from their only invocation point has mixed results on the readability of the larger function. It becomes more readable because it is composed of only short, descriptive function names, but less readable because deeper understanding of the intermediate steps requires disjointly bouncing around the code looking for the internals of the smaller functions.<p>The compromise I have often found is to reformat the intermediate steps in the form of control blocks that resemble a function definitions. The pseudocode below is not a great example because, to keep it brief, the control flow is so simple that it could have been just a chain of method calls on anonymous return values.<p><pre><code> AwesomenessT largerFunction(Foo1 foo1, Foo2 foo2)
{
// state the purpose of step1
ResultT1 result1; // inline ResultT1 step1(Foo1 foo)
{
Bar bar = barFromFoo1(foo);
Baz baz = bar.makeBaz();
result1 = baz.awesome(); // return baz.awesome();
} // bar and baz no longer require consideration
// state the purpose of step2
ResultT2 result2; // inline ResultT2 step2(Foo2 foo)
{
Bar bar = barFromFoo2(foo); // second bar's lifetime does not overlap with the 1st
result2 = bar.awesome(); // return bar.awesome();
}
return result1.howAwesome(result2);
}
</code></pre>
I make a point to call out out that the temp objects are scope-blocked to the minimum necessary lifetimes primarily because doing so reduces the amount of mental register space required for my brain to understand the larger function. When I see that the first bar and baz go out of existence just a few lines after they come into existence, I know I can discard them from short term memory when parsing the rest of the function. I don't get confused by the second bar. And, I don't have to check the correctness of the whole function with regards to each intermediate value.
I might be alone on this, but whenever I read things by John Carmack I get a vague sense that he doesn't really get object oriented programming. He always has a lot of interesting things to say, but it also kinda reads like a C guy trying to code in C++. I'm glad his thinking keeps evolving and he's not dogmatic about anything. I'd honestly love to hear his thoughts on C++11<p>"The function that is least likely to cause a problem is one that doesn't exist, which is the benefit of inlining it."<p>That's the equivalent of saying "the faster you drive the safer you are b/c you're spending less time in danger"<p>You'll just end up with larger monster functions that are harder to manage. "Method C" will always be a disaster for code organization b/c your commented off "MinorFunctions" will start to bleed into each other when the interface isn't well defined.<p>" For instance, having one check in the player think code for health <= 0 && !killed is almost certain to spawn less bugs than having KillPlayer() called in 20 different places"<p>I don't completely get his example, but I see what he's saying about state and bugs that arise from that. You call a method 20 times and it has an non obvious assumption about state that can crop up at a later point - and it can be hard to track down. However the flip side is that when you do track it down, you will fix several bugs you didn't even know about.<p>The alternative of rewriting or reengineering the same solution each time is simply awful and you'll screw up way more often
Haha, I like this quote: "That was a cold-sweat moment for me. After all of my harping about latency and responsiveness, I almost shipped a title with a completely unnecessary frame of latency."
I'm not a professional programmer and I rarely work with large code bases. So the fact that my code has drifted steadily over the years towards the large-main-function I thought was a factor of several things, first being my general amateurism. I still think that, but there are definitely other reasons too: I now use more expressive languages (Python instead of C) and more expressive idioms within those languages (list comprehensions instead of while loops) and more expressive structures/libraries (NumPy instead of lists of structures), so I can afford to put more in one spot. I also write smaller but more numerous programs.<p>But there are very real advantages. I learned through game programming and still do some for fun and I absolutely prefer having a main loop that puts its fingers into all the components of the game than to have a main loop which delegates everything to mysterious entity.update()-style functions. The lack of architecture allows me to structure the logic of the game more clearly for exactly the reasons Carmack outlines. Everything is sequenced - what has already happened in the frame can be seen by scrolling up a bit instead of digging through a half-dozen files.<p>But the real win here is for the beginner programmer. I strongly dislike the trend these days towards programming education being done in a "fill in the blanks" manner where the student takes an existing framework and writes a number of functions. The problem is that the student rarely has any idea what the framework is doing. I would rather not have beginners write games by make on_draw(), on_tick(), etc. functions but much rather have them write a for loop and have to call gfx_library_init() at program start and gfx_library_swap_buffers() at the end of a frame. That way they can say "The program starts here and steps through these lines and then exits here" versus having magic frameworks do the work for them. There is plenty of magic done these days behind the scenes for any beginner, but it is too much to have a completely opaque flow-control.
If anyone other than Carmack wrote this, I doubt it would be so well received so I'm glad he did.<p>We all have our own programming dogma that we love and defend religiously, but we should never stop asking if our code is truthfully, objectively, clear and easy to read, prone to bugs and/or runs efficiently. "Best practices" can get you 80% of the way there, but a developer should never stop questioning the quality of their code, even if it contradicts the sacred rules.
Back when I was writing real-time signal processing code, I spent quite a bit of time refactoring code from variations of styles A and B to style C. The problem Carmack talks about is even worse when some of those subroutines were in separate source files. Over the course of my refactorings, I was able to re-arrange and combine functionality in ways that were not possible with the discrete functions. I was able to pull operations out of inner loops that repeatedly and expensively calculated the same values. I found and fixed all sorts of actual or potential bugs in doing this. The original code was extra hairy, though, because it was written for DSPs, not general-purpose microprocessors.<p>As a side note, the original code contained large numbers of manual loop unrolling optimizations like noted in the email. I actually saw a performance increase from removing them. Same in some cases for manually inlining the function calls. From what I could tell, writing simpler, inline code made the optimizer much more efficient.
There's an interesting game programmer problem here, that is somewhat alien to a coder like me who grew up on the web. Where for a web coder, statelessness is the default, and we have to work to recover and recreate state between 'frames', game coders live in the run loop - and so the assumption is that you have a repository of persistent global state to act on each frame.<p>Having noticed that he has a problem when multiple functions are all interacting with that same shared global state, it's kind of amusing that Carmack's reaction is to reduce the number of functions, rather than remove the global state.
The following alternative style (vs. the 3 presented by Carmack) is one that I find very easy to read.<p><pre><code> algorithm() {
do_small_thing_one();
do_small_thing_two();
do_small_thing_three();
...
do_small_thing_X();
}
</code></pre>
Advantages over Carmack's Style C:<p>1. Substitutes comments for accurate and specific function names. Why better? Because comments can get out of sync with the code.<p>2. You can quickly see the sub-steps of the algorithm, rather than reading a multi-page-long giant function with a ton of comments.<p>When using this style, the inner functions are not visible outside of that source file (you can arrange this depending on your programming language). Then it's easy to make sure they are only called once within the source file, or only called appropriately.<p>That's because I agree with Carmack that functions called from lots of unrelated places are a terrible thing.<p>(Edited for clarity after people pointed out that it seemed like I was just advocating for Carmack's style A or B.)
A few questions: Are there any good examples of code written in this style (e.g. by Carmack or Blow)? When I tried this style, I would frequently end up with 800+ line functions: is this what the code is supposed to look like in the end, or should I be refactoring earlier? When I do end up refactoring, it's often hard to switch to a functional style. There are complex dependencies between the different "minor" functions, and the easiest route forward seems to be to replace the function with a class: minor functions become methods, and the function-level local variables become instance variables, etc. Also, this is a small issue, but how do you deal with minor functions that "return" variables? I typically wrap the minor functions in brackets, and I declare the "returned" values as local variables right before opening bracket, but it looks strange.
> "The function that is least likely to cause a problem is one that doesn't exist, which is the benefit of inlining it."<p>That statement (at least taken in isolation) is false. Inlining it means that <i>you're still executing the exact same code</i>. If it had problems as a function, it still has problems when inlined.<p>But that isn't the problem that Carmack is trying to address. He's concerned about bugs caused by lack of programmer comprehension of the code's role in the larger function. It's a valid concern. But inlining it makes it harder to find problems in the details of what the inlined function does (or even to realize that that's where the problem is, or maybe even to realize that there's a problem at all).<p>All styles help with some problems and make others worse. The answer isn't a style, it's good taste and experience to know when to use which style.
I'm assuming this was posted because it was brought up during Jonathan Blow's talk last night: <a href="http://www.twitch.tv/naysayer88/b/572153991" rel="nofollow">http://www.twitch.tv/naysayer88/b/572153991</a> (which have been interesting, (and Twitch is a great format for these))
An aside on tool interactions: For the past year or so I've been using a new way to do syntax highlighting[1][2] that works <i>really well</i> for highlighting the dataflow in large functions (whether well or poorly written): <a href="http://i.imgur.com/EmFMTtv.png" rel="nofollow">http://i.imgur.com/EmFMTtv.png</a><p>[1] <a href="https://medium.com/@evnbr/coding-in-color-3a6db2743a1e" rel="nofollow">https://medium.com/@evnbr/coding-in-color-3a6db2743a1e</a><p>[2] <a href="http://www.reddit.com/r/programming/comments/1w76um/coding_in_color/cezpios" rel="nofollow">http://www.reddit.com/r/programming/comments/1w76um/coding_i...</a>
The Saab Gripen aircraft that is mentioned did crash anyway (in its first flight show over Stockholm 1993 <a href="http://en.wikipedia.org/wiki/Accidents_and_incidents_involving_the_JAS_39_Gripen" rel="nofollow">http://en.wikipedia.org/wiki/Accidents_and_incidents_involvi...</a>) in spite of that specially crafted fly-by-wire flight software...
I found the functional programming (in C++) advice post linked from the referenced post a much more interesting read.
<a href="http://gamasutra.com/view/news/169296/Indepth_Functional_programming_in_C.php" rel="nofollow">http://gamasutra.com/view/news/169296/Indepth_Functional_pro...</a><p>"Avoid globals" is a fairly common (and good) truism for programmers of all stripes. But casting it in light of the central (and easily digestible) tenet of functional programming makes it much more approachable. Smart (but sometimes insufferably pompous ) proponents of functional programming should take notes.
Steve McConnell's classic "Code Complete: A Practical Handbook of Software Construction" cites a number of studies of real systems from the 1970s and 1980s with some surprising results. Some studies showed that function size inversely correlated with bug counts; as functions increased towards 200 LOC, the bug count decreased. Similar, another showed that shorter functions had more bugs per LOC and <i>reduced</i> comprehension for programmers new to the system. Another study showed that function complexity was correlated with bug count, but size alone wasn't.
There is an interesting parallel between Mr Carmack's "inlining" observations and one of the sessions I went to at Strange Loop this year. Jonathan Edwards was trying to beat back "callback hell" (i.e. unpredictable execution order leading to unpredictable side effects) by radically simplifying the control flow of his programs and keeping the execution model dirt-simple. Both would appear to argue that it's best to arrange heavily stateful code in a simple linear sequence, and use a top-down execution model, so that stateful effects are clear and easy to predict.<p>That being said, I've seen procedures that followed this sort of approach that were thousands of lines long. Even if we could have cut down on the ridiculous number of conditionals in that code, most of the state at that scale asymptotically approaches an undifferentiated mass of global variables. The result is testable and maintainable only via heroic effort. There have got to be limits to this kind of approach. (For Mr Edwards the solution was to break the whole thing up into a sequence of composable views, or lenses, with the interface between each stage being well-defined.)<p>I wonder to what extent Mr Carmack's pivot to pure functions is simply an acknowledgement that there were much better ways to refactor the code than the mess of one-timer procedures that probably seemed like a good idea the first time through...
Seems like everyone misreading the intent of the post. He is NOT advocating inlining code in this post. He is suggesting that FP is better at solving the same problems in a more sensible way.<p>The email was written in 2007. In there, he advocates the inlining of single-use functions into god functions as it reduces the risk of these functions being opted into other routines, especially when they all deal with shared mutable data.<p>Single-use functions are explicitly singled out in his email; he mentions that he does not encourage duplicating code to avoid functions.<p>| "In almost all cases, code duplication is a greater evil than whatever second order problems arise from functions being called in different circumstances, so I would rarely advocate duplicating code to avoid a function"<p>The blurb at the front indicates the intent of his post. Since then he has favoured a functional-programming approach - don't inline your functions, but avoid making your functions rely on mutable/larger scope states. Pass in everything that is needed by the function through parameters. Avoid functions with side-effects, encourage idempotence. That way, reusing the function does not lead to unintentional side-effects.<p>He also mentions that should you still decide to inline, " you should be made constantly aware of the full horror of what you are doing.".<p>A lot of things change within a decade. =)
<i>it is often better to go ahead and do an operation, then choose to inhibit or ignore some or all of the results, than try to conditionally perform the operation.</i><p><i>The way we have traditionally measured performance and optimized our games encouraged a lot of conditional operations [...] This gives better demo timing numbers, but a huge amount of bugs are generated because skipping the expensive operation also usually skips some other state updating that turns out to be needed elsewhere.</i><p><i>Now that we are firmly decided on a 60hz game, worst case performance is more important than average case performance, so highly variable performance should be looked down on even more.</i><p>Two words: Battery life. In case of mobile devices, this is not sound advice.
After reading this, I feel a lot better about the huge main() function I wrote for htop. I've always thought about splitting it into many functions, but somehow keeping it all flowing in sequence just made more sense.
I never wrote too many C or C++ on the desktop, but often ended up refactoring my embedded code from one style to an other.
After a while I realized this is simply my way of understanding the code better, and making sure I haven't missed anything.
The direction (inlining or breaking things to functions) almost doesn't matter. What matters is working with the code.
It's not so strange If you think about it, designers understand things by sketching and taking notes, that's why you see designers run around with their moleskins.
Is anyone else reminded of Facebook's Flux?<p><a href="http://www.infoq.com/news/2014/05/facebook-mvc-flux" rel="nofollow">http://www.infoq.com/news/2014/05/facebook-mvc-flux</a>
> If a function is only called from a single place, consider inlining it.<p>Funny because it's backward. If a code is duplicated, consider to make a function if the pieces of code are the same <i>semantically</i>. (Two pieces of code which are the same at a given time can diverge over time and you don't want to miss that. Only analyzing the sense of what you're doing (=semantic) gives you the answer.)<p>Never add fancy things (like adding a function which is not a function) in your code because code is not fancy, it causes bugs.<p>> If a function is called from multiple places, see if it is possible to arrange for the work to be done in a single place, perhaps with flags, and inline that.<p>Well yeah, fix the semantic if it needs to else do nothing.<p>> If there are multiple versions of a function, consider making a single function with more, possibly defaulted, parameters.<p>Well yeah, fix the semantic if it needs to else do nothing.<p>>Minimize control flow complexity and "area under ifs", favoring consistent execution paths and times over "optimally" avoiding unnecessary work.<p>The right thing to do is to <i>never</i> optimize unless it's to slow and you've identified the first bottleneck. "Never optimize" means: write the naive code correctly (without performance aberation like adding element in an array).<p>> To sum up:<p>Stop fancy. Stop optimization. Stop thinking about code syntactically (=the succession of operation gives the good result). Think constantly about your code semantically.
I don't have a great deal of experience tuning code, so would someone be able to explain how inlining is related to mutation of state? I'm not John Carmack or Brian O'Sullivan, and I'm not sure if I understand how purity would make things better.<p>We do inline our code in Haskell sometimes, but usually the real gains (in my limited experience, with numerics code) are to be had by unboxing, FWIW.
> If something is going to be done once per frame, there is some value to having it happen in the outermost part of the frame loop, rather than buried deep inside some chain of functions that may wind up getting skipped for some reason<p>> I do believe that there is real value in pursuing functional programming, but it would be irresponsible to exhort everyone to abandon their C++ compilers and start coding in Lisp, Haskell, or, to be blunt, any other fringe language.<p>"Here, let me dismiss functional programming, and by the way OCaml and other 'non-pure' functional languages don't exist, and functional programming languages aren't useful for anything 'real' so you should do your functional programming in C, and also you may want to dump everything in one long-ass function because I don't like deep stacks".<p>He's just rationalizing C traditions.
I believe in both functional and encapsulated patterns. It all boils down to scope of the task at hand. There is a certain kind of beauty in programming in a pattern than can compliant to a particular interface and a pattern that's efficient and scoped to the result required. Inline is a great way to encapsulate in a functional way.
Seems like a big argument against type C is that it would be more difficult to unit test the code. The nice thing about A/B (which are essentially the same to me) is that each subroutine becomes an easier target for unit testing.
Is this arguing that developers shy away from the forced OOP, and Patterns, instead relying on simple to read, step by step, functions?<p>One of my biggest gripe about OO programming was that you had no idea what the other components were doing unless you investigated each component directly. Sometimes the dependencies and the chain would get so large and complicated, you'd spend more time figuring out how to wrap your head around the whole thing than doing things that result in direct business benefit.<p>But every interview you go to will tell you otherwise, inflating technical debt is a great thing to keep managers keep their job and for sales team to boast about six digit LOC = Obviously state of the art.
How would you explain the benefits of functional programming with an employer who absolutely refuses to believe that OOP is overvalued? Lot of job requirements will say experience with OOP and then be asked to recite from memory what Singleton patterns look like or draw a Factory pattern as a yardstick of developer efficiency.