+3
Under Review

Pure function detection

Lazlo Bonin (Lead Developer) 3 years ago updated 3 years ago 4

In computer science, there is a notion of "pure" functions. There are many definitions for the term, but the simplest one would be that they have no side-effects and are therefore stateless.

For example, a function that adds two vectors and returns the result is pure.

As a counter-example, a function that takes a number and subtracts it from the player's health is not pure, because it modifies the state of the player.

If Bolt was able to detect which functions are pure, it could remove the control input and output port on the associated invocation units. These are often a big source of confusion for new users. 

For example, Input.GetButtonDown is pure, which means you don't need to connect its control ports and still safely retrieve the value output port. However, there is no clear indication of that in the UI, and new users will end up creating control connections that clutter their graph for no reason.

The problem is that C#/.NET language analysis and reflection does not provide a way to determine whether a method is pure. We could introduce a [Pure] attribute for custom user methods, but that wouldn't apply to built-in Unity methods, for example. It seems that the only way to support that would be to manually analyse each Unity method and store it in a "pure functions" bank, but that is incredibly tedious and prone to break on Unity version changes.

Bolt Version:
Unity Version:
Platform(s):
Scripting Backend:
.NET Version (API Compatibility Level):

As someone who does functional programming, I'm down for anything that adds functional features. As a matter of fact, I would love to use F# and if you think the reflection you're using (I assume you're using expressions with Emit?) would work, please let me know. I can whip up a project and test it out next week.

I'm not sure I understand though, `Input.GetButtonDown` is not pure. It's not referentially transparent, meaning that if you pass the same inputs, you will not get the same outputs. While it may not be producing side effects, it is reading global state. Functions that access global state are therefore impure, because the output will depend on the current state.

I assume that the control input/output ports you mean the green arrows? Then yes, I could see them not being needed, since you'd basically be piping the outputs into inputs. However, this doesn't feel like it's an issue with "purity", but feels more like it's just an issue with no way of handling "Unit", as in the "void" return type found in functional languages. Just an FYI for anyone reading this, all functions in a functional language return a value. All of them. The ones that return "nothing" return a type called "Unit", not to be confused with bolt's terminology.

So I can see the need for these input/output control ports because of the design's imperativeness, but maybe there's a more general way to disable them, without requiring "Pure".

I agree, as I started by saying, there are many definitions for "purity". Maybe this should be called "side-effect-free-functions" or "pipe functions".

I like the green stuff. If it wasn't there, you would have to know more about the stuff going on in the background and for new users like me it was helpful. Maybe being able to switch between easy mode, intermediate and advanced would be great, so thumbs up from me.