What are the reasons, technical or otherwise, that pure functions (functions which do not rely on external state and have no side effects) should not always be public and static?
When a static and pure function is very general, such as Math.floor()
or something, then it makes total sense to have it be public and static. Why shouldn't we have global access to basic math functions? But when you do the same with functions specific to the application domain, specific even to JUST the class they are implemented in, then I begin to feel like I'm doing something wrong.
For example, let's say a we have a class User
, and it has a super specific attribute called happiness
, which can be changed by eating food. Generally I would have a public function acting as an interface, getting the outside information, then call a protected helper function which is purely implemented, passing in any stateful data as arguments. The implementation might look like this:
class User {
private FoodPreference foodPreference;
private float age;
// ...
// constructors initializing the users food preferences and
// changing the Users preferences as they get older
// ...
// Impure interface relying on users state to get their preferences
public void EatFood(Food food) {
this.happiness = this.ComputeHappiness(this.foodPreference, food);
}
// Pure, does not rely on Users state to compute this info, it's passed in as an argument.
protected Happiness ComputeHappiness(FoodPreference preference, Food food) {
Happiness newHappiness = /* some user-specific logic computing their happiness due to eating food */
return newHappiness;
}
}
If ComputeHappiness()
was public and static, would that be bad? My knee-jerk reaction is "Yes it would be", I think because it seems that the usage of the keyword static in OOP is something generally avoided since it is so easy to shoot yourself in the foot.
I can see a lot of benefits to having pure functions be static and public, even if they are not very general:
- Pure functions are easier to test. Static functions are easy to access. Your unit tests will need to do less setup to produce the means to test the function. Now you just need the data, which usually isn't the hard part of testing functions.
- You public functions can be interfaces which access the stateful data (from the object, database, or any other source) and leave the business logic to their purely implemented helper functions.
- Pure functions can be reused much more easily. Perhaps other parts of the application would like to ComputeHappiness()!
What are the reasons this is a terrible idea? It certainly seems to violate the principle of least astonishment.