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?

4
  • What is getValue? Not defined nor called anywhere.CommentedApr 7, 2019 at 21:11
  • @JesseC.Slicer typo fixed. It's valueGetterCommentedApr 8, 2019 at 1:13
  • thanks! It's a rather neat solution.CommentedApr 8, 2019 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.CommentedApr 8, 2019 at 3:25

1 Answer 1

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.

3
  • 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
    CommentedApr 8, 2019 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.CommentedApr 8, 2019 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
    CommentedApr 8, 2019 at 8:43

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.