Conversation
Edited 10 days ago
kas's thoughts on pure vs. impure functions
Show content

I’ve been thinking about the concept of pure functions a lot lately. at first I wanted to dismiss them but knew that I didn’t have enough experience with them to really make a judgement, but now I think that I’ve tried them enough to be able to give my thoughts

so first of all, pure functions are useful because they can be easily tested as self-contained code, which makes both debugging and unit-testing easier

but, I would argue that a codebase full of thoroughly unit-tested impure functions is actually much easier to debug than a codebase full of non-unit-tested pure functions. simply because you can immediately rule out all unit-tested functions as the cause of the bug (unless the unit-tests are missing something important, which I’ve never run into personally)

pure functions are very easy to unit-test, but impure functions with simple and well-documented side-effects are roughly as easy, and IMO they tend to be much more ergonomic overall: they’re generally easier to write, read, understand, and use (assuming that this is a function where one would be tempted to have side-effects, of course)

the one exception to this IMO is when you’re mutating shared state that isn’t neatly contained inside of an abstraction like an object. for example if a function takes a data-structure and then mutates that data-structure, you have to trust the caller to know that it’s okay for all references to that data-structure to be mutated. but in that case my solution would just be to make a class for that data-structure instead. because then it’s much easier to conceptualize what that state represents, and when it’s appropriate to make a copy of it instead of changing the original

I feel like there are plenty of potential footgun situations for a caller thinking that they have the only reference to some data-structure, mutating it, and then realizing that some code somewhere else was relying on the same assumption (that they have the only reference to that data). but again I think that the best solution is often just to make a new class to hold the data instead of treating the data itself as immutable

so basically I think that limiting the un-abstracted complexity of a function’s side effects is often a very good idea. but I think there are diminishing returns to limiting side-effects, and trying to maximize the number of pure functions in a codebase is more trouble than it’s worth - both for the caller and for the function-writer

2
2
3

eli, vampire kitsune

Edited 10 days ago
re: kas's thoughts on pure vs. impure functions
Show content

@kasdeya this approach meshes well in game development too where pure functions are great when they’re basically nothing more than utility functions, but given the scope of a game, it’s really hard to make it entirely pure. in fact, a lot of “bad practices” are extremely useful in game development – including the beloathed singleton pattern which is everything bad about global state as an object. in these cases, i think that conceptual simplicity isn’t as simple as “this function is pure” or “this function is impure” because there’s the entire architecture of context hanging around it. sure, a pure function is simple in a vacuum – but what about an entirely pure serialization/deserialization system which needs to capture the state of the entire game and write it to disk? it’s much easier to just have some horribly global, mutating object which knows too much and call SaveSystem.save()

this is starting to ring like the last time i replied to one of your programming posts though. i think context matters a lot when it comes to evaluating the utility of a programming paradigm

0
1
3
kas's thoughts on pure vs. impure functions
Show content

@kasdeya yeah we've found pure functions are most useful when they're used as part of a broader code structure / architecture

trying to make a whole web request endpoint handler pure? probably not going to happen, we need to reach out to the database to get stuff, there's logging and performance metrics stuff going on, not to mention the actual action the user is trying to take . . .

but stuff for "extract the authorization headers from a request", "check that this user has permission to do this action on that resource", "transform this database response so it can be sent to the client" — these are things that are going to get utilized basically everywhere, and structuring the rest of the application so those can be as close to pure as possible does help a lot with testing and application consistency

0
0
2