Skip to content

3. Additional Features

Max Pixel edited this page Mar 5, 2021 · 4 revisions

Selective Execution

TLDR: The Null Coalescing operators will only cause pure functions to be executed when absolutely necessary.

"But Mr. Pixel," you might find yourself thinking, "you can accomplish the same thing using a Select node, without having to invent a whole new Blueprint node!". In certain circumstances, yes, a Select node can yield the same results in a more concise manner than the examples above:

You could even make a macro out of it!

... however, there's a huge difference as soon as you need to introduce a pure function or non-local/non-member variable as any of the inputs. Consider the following:

The FindFoo and FindBar functions log when they are run. When run, what do you think the log will produce? Let's see:

Finding Bar...
Finding Foo...
Result: Foo

Uh oh! It ran both functions, even though it really only needed to run FindFoo. Now, let's replace that logic with a Null Coalescing Operator:

Now the log looks like this:

Finding Foo...
Result: Foo

The Select node causes all inputs to be fully evaluated every time, and there's absolutely nothing you can do to make it behave otherwise. Select is implemented as a single (relatively heavy) bytecode operation in the Blueprint VM, which means that all inputs must fully evaluated before it's possible to perform the Select operation. The Null Coalescing Operator, on the other hand, will wait to evaluate input B until it's certain that input A is null. Thanks to this feature, you no longer have to compromise between code-conciseness and performance in this sort of situation.

Smart Type Inference

The order in which you connect nodes to a Null Coalescing Operator doesn't matter: As long as the end result is valid, you will be able to do it. Conversely, you are prevented from making connections that are invalid, as soon as it is certain that they are invalid (to the extent that the Blueprint Node API currently allows).

Technically speaking, the node performs type inference in a way that treats connections as a generic type for which the input connections are covariant with the input type resolving to the most specific output type, while output connections are contravariant with the output type resolving to the least specific input type.

The same cannot be said for any of the built-in wildcard-sporting nodes. With the Select node, for example, as soon as you connect any of its wildcards, then all of its wildcards will adopt the type of that connection. This means that if you want to choose between an Object and an Actor...

... you had better connect the Object pin first, because if you connect the Actor pin first...

... then you will be prevented from connecting the Object pin, because it isn't an Actor.

Conversely, if your output is to be connected to receiving Object and Actor pins, then you'll have to connect the Actor pin first:

With the Null Coalescing Operator, on the other hand, even after connecting an Actor input, it still allows you to connect a less-specific type to the other inputs (unless you already have an output connection that isn't assignable from that less-specific type). And even after connecting the Output to an Object pin, you are still allowed to make a new connection to an Actor pin from that same output (unless you already have an input that isn't assignable to Actor).

Variadic Pins

Most of the time, null coalescing is used to decide between an optional and a default, but there are totally valid situation in which you may want to decide between more than two options, such as when implementing pseudo prototypal inheritance. The "Add pin +" button in the corner of the Null Coalescing Operator allows you to do just that:

If you've accidentally added more pins than you need, you can clear out the unused ones from the context menu: