6

I have heard some people claiming that boolean state variables are generally bad and should be avoided when possible. Apparently in many cases it is possible to put state into lambda functions, instead of using booleans. An example to this approach is a conceptual implementation of C#'s Lazy. Lazy<T> gets a function-like object that constructs T the first time Get() is called. Subsequent calls to Get() return the same T object over and over again.

For example, a trivial implementation of Lazy, which uses bool can be found at this SO answer. Here is a simplified (thread unsafe) version:

class Lazy<T>
{
    private readonly Func<T> createValue;
    private bool isValueCreated;
    private T value;

    T Get()
    {
       if (!isValueCreated)
       {
           value = createValue();
           isValueCreated = true;
       }
       return value;
    }

    public Lazy(Func<T> createValue)
    {
        this.createValue = createValue;
    }    
}

An alternative is to get rid of bool and use lambda functions as a way to store state:

class Lazy<T>
{

    private Func<T> valueGetter;

    public Lazy(Func<T> createValue)
    {
        valueGetter = () =>
        {
            T value = createValue();
            valueGetter = () => value;
            return value;
        };
    }
    public T Get() { return valueGetter(); }
}

I find both variants similarly readable, but the second variant can be too surprising to some programmers, even though the second is shorter. Due to the surprise factor I like the second variant less. The same can also be done with C++:

template <class T>
class Lazy
{
  public:
    template <class U>
    Lazy(U createValue)
    {
        valueGetter = [this, createValue]() {
            T value = createValue();
            valueGetter = [value] { return value; } ;
            return value;
        };
    }
    T get() { return valueGetter(); }

 private:

    std::function<T()> valueGetter;
};

I have never seen anybody suggest the above, so I assume that it is less fashionable to use this construct in C++.

I find the above lambdas more appealing in a "neat trick" sort of way, but not in a "work with other people" mentality. Should booleans be avoided when lambdas can be used like that? Is using lambdas, like that, considered a good design? Have I been programming C and C++98 for too long, and have become tainted by the mentality of those languages for preferring the boolean?

  • What is getValue? Not defined nor called anywhere. – Jesse C. Slicer Apr 7 at 21:11
  • @JesseC.Slicer typo fixed. It's valueGetter – Michael Veksler Apr 8 at 1:13
  • thanks! It's a rather neat solution. – Jesse C. Slicer Apr 8 at 2:12
  • @JesseC.Slicer yes, it is a neat solution. I have seen it floating around over a year ago, and could not remember where I saw it first. It is not my solution, but I can't point to the origin. – Michael Veksler Apr 8 at 3:25
8

Using these kinds of object-oriented or functional techniques can be super neat and elegant. If you need a fancy name for what you are doing here, I suggest the State Pattern, with function objects representing the state.

But there are two objections to such approaches:

  • They are less obvious. Simple is good. A closure that re-assigns itself to a different closure isn't exactly simple. Here, it seems to introduce more complication than it removes.

  • They are less efficient because they imply an additional level of indirection: the techniques require indirect calls that are difficult to optimize. In contrast, a conditional is very easy to optimize, especially if the compiler can be told that the condition is likely to be false.

However, this depends on context. Adding a boolean field makes the object body larger which could lead to worse cache usage, thus impacting performance more noticeably. A Lazy implementation provided by the runtime can possibly avoid any space overhead with tricks such as tagged pointers, or with objects that can change their type (from the lazy thunk to the target value).

As an alternative to using a boolean, it may be possible to restrict the value T to non-null reference types. Then, a null check is sufficient to determine whether the object needs to be constructed.

  • 1
    An old programmer once told me they used to implement things like this by having the function write a jmp instruction into its own code the first time it was called, thus skipping it on subsequent calls. The lambda version of the code has a similar feel. If the compiler could optimize the lambda assignment to a jump (perhaps with the aid of the JIT), it would be similarly efficient. – Owen Apr 8 at 2:08
  • @Owen nice observation self modifying code is frowned upon in the c++ world, due to security concerns. However, in the JIT world it makes sense. – Michael Veksler Apr 8 at 7:38
  • @Owen the .NET CLR uses such tricks to optimize method calls that go through an interface type, which does apply here. However, the Get() method body is effectively shared between all Lazy<T> instances where T is a reference type. It cannot be specialized to the state of a particular instance. And any speculative optimizations would require guard conditions that check the lambda's type. Within the constraints of the CLR, there is no way the lambda based approach can be as fast as a conditional based approach. It would be possible though to construct a C++ solution with function pointers. – amon Apr 8 at 8:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.