Functional Programming

John Carmack (founder of many first 3D games like Wolfenstein 3D, Doom, Quake) wrote an interesting article on functional programming, and this article further summarizes it, and utilizes examples of how this practice can be applied to the code.

Functional programming (FP) is a paradigm, and you do not need a particular language to use FP. We do not necessarily need to understand the history, or how particular languages like Scheme or F# works. In this article, we will study how we can apply this paradigm to C or C++ code base.

As a simple example, consider the following code, and note the following:

  • increment_and_wrap() accesses its own internal state (m_capacity)

  • It modifies the input variable directly

size_t get_count_max() const { return m_capacity; } void increment_and_wrap(size_t &value) { ++value; if (value >= get_count_max()) { value = 0; } }

If we were to translate this code to FP, it may look like the following; note that:

  • increment_and_wrap() clearly expresses its inputs and outputs

  • Parameters are not modified, and a new value is returned

size_t increment_and_wrap(size_t value, size_t max_value) { ++value; if (value >= max_value) { value = 0; } return value; }

In a unit-test environment, it would be easy to test the FP based increment_and_wrap():

void test_increment_and_wrap(void) { assert(1, increment_and_wrap(0, 3)); assert(2, increment_and_wrap(1, 3)); assert(0, increment_and_wrap(2, 3)); }

Application to C++

One way that you can guarantee that you are applying the FP paradigm is to use const functions, which by definition imposes that the functions are pure and that they do not modify an internal state. Furthermore, also realize that non-const functions are also not thread-safe, and:

the ability to incrementally poke and prod objects into unexpected states is indeed a significant source of bugs

Terms

Pure Functions

Pure functions accept parameters, and return one or more computed values. They do not operate on any internal state such as the m_ member variables of a class, and they do not read or update a global state, nor do they maintain any internal state.

De-coupling functions from specific internal variables promotes code re-usability, makes the code easier to read, and helps facilitate testing.

With C++ classes, you may be tempted to write “impure” functions because a class fundamentally helps you hide the data as private members, and it may seem natural that class functions can access the private members directly.

Conclusion

Whether it is C or C++ code base, the FP paradigm should be encouraged.

  • FP helps decouple the code, and allows the code to be more reusable

  • FP helps read the code more clearly as you can see the parameters, and output easily (facilitates better code reviews)

  • FP eases the unit-test development

John Carmack’s conclusion was:

No matter what language you work in, programming in a functional style provides benefits. You should do it whenever it is convenient, and you should think hard about the decision when it isn't convenient

Action Items

Inspired from John Carmack’s article:

  • Survey your code-base and identify pieces of code that can use FP paradigm

    • You should be easily able to spot code that can improve its readability and refactoring into a pure function would better describe the inputs and outputs, and hence make the code easier to read

  • Refactor code into pure-functions to realize the benefits, such as:

    • Easier to unit-test

    • Modular code base (loosely coupled)

SIBROS TECHNOLOGIES, INC. CONFIDENTIAL