Pure functions

Dear Epic,

this thread motivated me to “complain”:

From the thread:

The term “pure” is misleading. So far I did not find any information on whether there are any plans regarding this.

Most of the bugs I write and find are due to ‘pure’ nodes

See the thread for examples or this one:
(The SetActorRotation node uses the new actor location as basis so the rotation will be off by 1 degree)

Now having ‘pure’ functions without execution pins does result in less links. I don’t think that this is worth it.

I propose:

  • Keep the concept of pure functions due to potential optimization benefits

  • Add execution pins to const functions (getters etc.) but allow the user to collapse them (similar style to pure nodes but with clear visual hints that the node is not a pure one)

  • the output pin of a collpased node may only be linked to one other input pin

  • a collpased node may only have one return value. Nodes with multiple return values may not be collapsed or need to introduce custom structs for their return value). This is in order to avoid connecting the individual output pins to multiple nodes

I would love to hear feedback on this.

Best regards

I haven’t checked this, but does the editor enforce function purity in any way? Also, do the pure functions take advantage of memoization?

No the developer can choose to set the purity. The developer can specify a function as pure even when it calls impure ones.
Since it is not enforced the system can’t take advantage of memoization.

Ah. There goes referential transparency then.

You are correct that our use of BlueprintPure isn’t exactly a match for purity in functional languages. The contract states only that there will be no side effects, but many BlueprintPure methods can and will consume object or world state:

/// This function fulfills a contract of producing no side effects, and additionally implies BlueprintCallable.

BlueprintPure is a contract that a programmer promises to adhere to, but the C++ implementations are not verified as we have no realistic method to enforce it in C++. If you see any methods marked BlueprintPure that have side-effects, please file that as a bug.

Michael Noland

Okay, but what is the rationale behind this?
We have less execution pins in the blueprints at the cost of multiple calls per output value reference.
Except for brevity - is there an advantage I am missing?

Can the MakeArray node be considered buggy? It allocates a new array and that is considered a side effect, right?

Not having to schedule execution of BlueprintPure methods is the entire point of them; doing any kind of complex expressions in a visual language is a nightmare otherwise. There is no automatic memoization or other optimzations applied to BlueprintPure methods, although it is easy to do so yourself by right-clicking on the output and promoting it to a variable, which you can then schedule as desired.

No, it just returns a value, it has no side effects.

To clarify: Side effects mean observable state from other gameplay code. Allocating memory where the only reference to the memory is the result of the node isn’t considered a side effect for this purpose, even though it can technically be observed if you had access to the memory allocator / were able to ask how much memory is free.

Michael Noland

Just so I understand correctly, it would be an optimization to set a variable using the pure function prior to plugging it into whatever it was built for?

Running a pure expression tree into a set node and then using the result only once would be a tiny bit slower. If you use the result of the tree more than once (and know that no queried values inside of it have changed), then saving it to a variable and reading that variable is going to be faster.

Michael Noland

Here’s an example of saving off the value (right-clicked on Return Value and promoted to a new variable called New Rotation).


Note: In this case with three nodes using the calculated value, it is faster, but it can also change the behavior. I picked this example to show off another thing about the ‘not quite pure’ nodes. Since this uses GetActorRotation, it depends on world state, so if Self were Actor1 or Actor2, then doing it without the Set call would cause the remaining actors to be at a different rotation, instead of all three being identical.

Michael Noland

Can’t we just add a check box option to each pure node so that it only runs the first time it is called (then save the result to a temporary variable)?

That way since it’s optional, it won’t need to save any variable if it is unchecked while also being more convenient by not having to manually save the result to a variable.

Just an idea.

The first time when? First time ever, first time per frame, first time per event, etc…? There is too much ambiguity with doing something automated like that, and then people would want or need an exec input pin too, in order to clear the latch flag. Doing it explicitly with a variable set uses an existing concept and it is immediately obvious and controllable when it happens.

Michael Noland

What about optionally adding exec pins to pure nodes?
Because in the end the only advantage of pure nodes is that they don’t have execution pins. And at the same time this can be one of their worst disadvantages. So let the developer decide what to use.
It would not introduce a new concept and it would not break any existing code.

I like this idea a lot better. I think we’ve discussed it before but it never got filed since there’s an easy work around with promoting to a variable; I’ve entered it as a feature request.

Michael Noland

So center of mass being a pure function doesn’t have execution pins. I’m attempting to highlight my sphere of interaction to allocate items or weapons while in the field of play. I cannot interact with em in order to enter my inventory. Itd be a tad bit gratifying to accomplish this. Alongside getting some item default images without royalties. Possibly a blendr thing. Anyhow, How do I get the Center of mass function to mimic this tutorial at 1:20seconds? It requires execution pins and a yellow triangle beneath the right hand side execution pin. Thank you a lot.