Last Revision — April 19, 2022
2 Min Read
Impure Functions
Functional Abusers
Responsibility
Within a Class
- Mutable Data (caused)
- Dubious Abstraction (causes)
- Binary Operator in Name (antagonistic)
- Flag Arguments (antagonistic)
Marcel Jerzyk in thesis (2022): "Code Smells: A Comprehensive Online Catalog and Taxonomy"
The first or second most essential functional programming principle (interchangeably, depending on how big we want to set the statement's tone) is that there be no side effects. Object-Oriented programming can apply this rule, too, with great benefits.
In a perfect scenario, when looking at a higher abstraction set of method calls, even an inexperienced bystander could tell what is happening more or less. The code example appears to receive a player object identified by Marcel Jerzyk, sets its gold to zero, and manageable health status. That is great because one can make reasonable assumptions about the code... unless one cannot due to the side effects, which make these methods impure. By taking a closer look at the set_gold(amount)
function, it turns out that, for some reason, this method triggers a dancing animation and resets the payday timer... of course, if one did not lose his trust yet, that the method names are representative of what they do.
The method and function names should tell what they do and do only what is anticipated to maximize code comprehension. I want to note that developers should fix this by removing the side effects to separate methods and triggering them individually, not violating the Single Responsibility Principle. Changing the name to set_gold_and_reset_payday(amount)
, would create Binary Operator In Name Code Smell and another possible bad solution, set_gold(amount: int, is_payday: bool)
, would cause Flag Arguments Code Smell.
Developers added additional actions to existing functionalities, which usually took place in the context during typical use and are not explicitly related to the function itself.
The method is doing more than one thing.
@dataclass
class Player:
gold: int
job: Job
def set_gold(self, amount: int):
self.gold = amount
self.trigger_animation(Animation.Dancing)
self.job.reset_payday_timer()
marcel: Player = find_player_by_name(Marcel, Jerzyk)
marcel.set_gold(0)
marcel.set_health(Health.Decent)
@dataclass
class Player:
gold: int
job: Job
def payout_routine(self):
self.trigger_animation(Animation.Dancing)
self.job.reset_payday_timer()
def set_gold(self, amount: int):
self.gold = amount
marcel: Player = find_player_by_name(Marcel, Jerzyk)
marcel.set_gold(0)
marcel.payout_routine()
marcel.set_health(Health.Decent)