Pure functions have side-effects

Functional programming emphasizes “pure” functions, functions that have no side effects. When you call a pure function, all you need to know is the return value of the function. You can be confident that calling a function doesn’t leave any state changes that will effect future function calls.

But pure functions are only pure at a certain level of abstraction. Every function has some side effect: it uses memory, it takes CPU time, etc. Harald Armin Massa makes this point in his PyCon 2010 talk “The real harm of functional programming.” (His talk is about eight minutes into the February 21, 2010 afternoon lightning talks:  video, audio.)

Even pure functions in programming have side effects. They use memory. They use CPU. They take runtime. And if you look at those evil languages, they are quite fast at doing Fibonacci or something, but in bigger applications you get reports “Hmm, I have some runtime problems. I don’t know how to get it faster or what it going wrong.

Massa argues that the concept of an action without side effects is dangerous because it disassociates us from the real world. I disagree. I appreciate his warning that the “no side effect” abstraction may leak like any other abstraction. But pure functions are a useful abstraction.

You can’t avoid state, but you can partition the stateful and stateless parts of your code. 100% functional purity is impossible, but 85% functional purity may be very productive.

Related posts:

Using Python as a functional language
F# may succeed where others have failed
Functional in the small, OO in the large

21 thoughts on “Pure functions have side-effects

  1. John, I disagree with that citation. I don’t know the whole context of his statements, but for my point of view the “No side effects” tenet of FP is not about not taking some RAM or using some CPU. So while he’s right in what he says, I believe that the FP jargon doesn’t means what he means.

    Sorry if my english is too confusing :D

  2. Alvaro: Right, when most people talk about side-effects they mean side-effects in the logical state of the program in memory. But I think the point being made is that pure functions DO affect the state of the machine they are executing on (BUSY / NOT-BUSY / etc) regardless of whether they affect the logical state of the program and you can never get around this problem completely because we will never have an infinite number of cores to deploy functions to nor an infinite amount of memory and memory bandwidth.

  3. Alvaro: Your English is fine.

    I don’t think Massa disagrees with the FP community about what the stateless abstraction means. He disagrees about its usefulness. FP calls a function “pure” if it has no side effects of a certain kind. I think what Massa is saying is “Yes, but the kinds of side effects that you leave out of your abstraction become important as programs scale. Also, I think your effort to make things stateless, even according to your abstraction, is misguided.”

  4. I think this is a brilliant observation. It also frames one of my complaints about attempting to learn functional languages. It seems like every time I try, the books start out by explaining how wonderfully expressive and natural the functional language in question is. But before you get very far, they start explaining how you need to uglify your code in order to make it fast enough to use in practical situations.

    Fibonacci is a great example, because there is a very natural recursive definition which is completely useless for real world calculations. It is pure in the functional sense, but it is too damn slow to be practical, and naively trying to use it in your code is akin to performing a denial of service attack on yourself.

    It seems like what we really should be doing is trying to minimize side effects of all sorts, not just those that functional programming recognizes directly….

  5. You are very right. Every abstraction has a danger when you assume that an abstraction is absolute. You should understand whenever working with abstractions, that there is a layer of abstraction on which you are working. But if that layer of abstraction isn’t there, then our jobs will be a lot tougher. For instance, if there were no layer of abstractions, then we would still be coding in binary.

    Nice Reminder

  6. i must say i never really started to understand the idea of functional programming, or why i should do things according to that kind of thinking. maybe i am too stupid, maybe i haven’t read the right book or met the right guy, yet. so i am very much a procedural, dynamic typing programmer. on the other hand, i used to be an object oriented programmer as well, but in the past four to two years, i have completely changed my style; now i do it the data-centric, library-oriented way. which means that on one hand, i deal with generic structures that represent things, whatever, say that ubiquitous car from OOP intros, which would perhaps look like { '~isa': 'car', 'weight': { '~isa': 'quantity', value: 750, unit: 'kg', }, 'wheels': [ { '~isa': 'wheel', ..., }, ... ], }, you get the idea. so that is an object, and state is broken down to very generic datatypes; assembly is done with dictionaries, sets, lists, that kind of stuff. in order to work with these things, i write generic and specialized libraries of procedures (methods). libraries do not have state, or let us say, in the typical case a library can depend on a configuration, that typically does not change over its lifetime. this configuration is itself stored in passive data structures. again, typically, since libraries have static state which is their configuration, you can instantiate one library when the application starts, put it into the global namespace, and leave it sit there waiting to be used by whatever procedure chooses to call one of its methods. it is only the data objects that are passed around, that change, that are constantly created and destroyed. i find this style of programming tremendously clearer, easier and pragmatic than classical OOP; i am so much more productive with it, for a number of reasons.

    now reading your post, i realized there is, after all, something that links this style and FP: those libraries do not change. they do not alter their own state when one of their methods is called; their methods are FP ‘pure’ functions with respect to their own state. so it is like part of the perceived usefulness of the scheme described is directly linked to precisely this property.

  7. I recently heard someone describe imperative and functional programming by saying that imperative programming sends the data to functions, but functional programming sends functions to the data.

  8. The idea “stateless is better than stateful” comes about very naturally in imperative programming too, once you start thinking about concurrent programming.

    It is not possible to avoid having a state, but at least try not to share it between threads.

  9. I agree with Massa. Just by executing, a “pure” function modifies the environment that it’s running in. Here’s a concrete example: In XNA, (a .net game framework) the main render function should get called 60 times a second for the game to render smoothly. Unfortunately, garbage collection typically takes too long for it to execute and a frame to completely render, which results in pauses in the game, so a lot of XNA games are optimized to not allocate objects at all during gameplay.
    So, if you have a function with “no side effects” that allocates objects, you’ve got side effects. Even with traditional compiled languages, you get side effects via your function’s interacting with the memory manager and memory caches. Typically, it doesn’t matter, but sometimes (like with XNA) it does. And when it does, you need to be aware of how your code interacts with the environment that it executes in. I think that was his point.
    Moral: There’s no silver bullet.

  10. “Hmm, I have some runtime problems. I don’t know how to get it faster or what it going wrong.”

    Personally this sounds like a straw man argument – as if he tried doing it once and just gave up, but granted, I don’t know the whole context so I may be very wrong here.

    Personally, I find pure abstractions very reasonable for things relating to optimization at least in Haskell – many times I’ve needed more efficient code, I can just pick a more optimized library. The fact that many of these libraries have a pure interface makes replacing the slower, also pure ones, easy to test and make sure I got it correct (examples: containers/unordered containers, string/bytestring, array/vector.)

    No programming language is ever going to preclude the need for good engineers to know their environment, from their compiler and OS to their programming languages and their implementation. Python, functional programming or not.
    I don’t think anybody reasonably believes that pure functions don’t use RAM, or CPU time. That’s an obvious statement – the point of pure functional programming is that the reasoning benefits granted by it are a boon in many cases. Not that it suddenly means we don’t have to worry about the real world at all.

    I’d agree “100% pure functional programming” isn’t possible – that’s why it’s not advocated (by anyone that isn’t crazy, at least.) The point isn’t to eradicate mutable state, or win some war. It never was – it’s to control such effects and make them easier to reason about, by leveraging things like the type system. That’s what pure functional languages like Haskell allow you to do, with, in my opinion, great benefit. It just turns out lots of problems can be modeled in a mostly pure way.

  11. I didn’t watch his presentation. But from the description he is mixing abstraction levels and attributing a change in the environment to the wrong effector.

    All computation on the machine modifies its environment. No where in the abstraction contract is there even a description nor a mention of what the code does to the machine, that is up to the runtime and the hardware. FP is a mathematical and structural contract that says nothing of the runtime behavior. FP will not magically make all of your code O(1).

    Equally, non-functional code also modifies the state of the machine, in much the same way.

    If he wants a formalism that describes both referential, structural or temporal transparency (perhaps he doesn’t) he needs more than fp.

    Sounds like many arguments that boil down to, “yeah and but …”

  12. Physically, functional programming may be an important tool in decreasing the amount of energy needed to perform a computation. This is because a functional program can be executed without erasing information. And it is the erasing of information which causes energy dissipation (aka heat).

    On a related topic, I think that a quantum computer would be easier to program using FP, because the natural language of programming quantum computers are unitary (reversible) transformations:

    Psi(t) = U Psi(0).

    Which is quite functional, when you think about it for a while ;-)

  13. The argument is true. Actually, there are side effects. That’s the way the whole universe is built, anyway. However, the argument is not convincing. Or should we now give up all abstraction layes? A compiler or an interpreter have side effects, so do CPUs. I expect abstraction layers like algebras or functions to raise the abstraction level in such a way so that the developer is protected from the mutable reality. It should be the responsibility of a few language implementers to take care of this issue instead of thousands of language users. Of course, transparency could lead to shooting yourself in the foot. Thus, developers should be aware of the physical underpinnings of their platform. And thus they also will know good reasons whenever they need to deviate from the pure functional perspective.

  14. its an amusing but pointless observation. the abstraction of a pure function still has value, there have been no balloons popped here

  15. QuantDev wrote:

    And it is the erasing of information which causes energy dissipation (aka heat).

    Actually, any logically irreversible operation results in heat dissipation; which includes deletion but also copying. See, for instance, Landauer’s principle:

    Any logically irreversible manipulation of information, such as the erasure of a bit or the merging of two computation paths, must be accompanied by a corresponding entropy increase in non-information bearing degrees of freedom of the information processing apparatus or its environment.

    Thus, copying a data structure during functional computation, when it results in overwriting bits, results in an entropy-increase. I realize that you said, “a functional program can be executed without erasing information” (emphasis mine); and not that it will or must. But, practically speaking, even a functional program will be in the business of entropy-augmentation.

  16. Just wanted to highlight this part of the post:

    I appreciate his warning that the “no side effect” abstraction may leak like any other abstraction. But pure functions are a useful abstraction.

  17. Here’s the thing: no one is selling functional purity and side-effect-free-ness as tools to maximize runtime predictability. They’re sold as tools to maximize the effectiveness of certain kinds of reasoning about program structure and correctness under the old guideline that programmer time is more valuable than computer time.

    No one disputes that this type of reasoning feels less natural to some people and some problems are a better fit for the paradigm. But the real side-effect of FP is the change in the mindset for the programmer.

  18. I seem to be late to the party, but I want to throw my 2¢ in anyway.

    Telling the computer HOW to do something is different from telling it WHAT to do. It’s as if we have a slider, and if we drag it to the WHAT end, we get to describe our problems to the computer and have it figure out how to solve them for us, and if we drag it to the HOW end, we tell the computer how to solve our problems without it knowing what the problems are.

    One way of looking at pure functional programming is that it’s just imperative programming, yanked a few notches toward the WHAT end of the slider. When you write a pure function, you theoretically have no idea how it’s going to be executed. I mean, you can guess based on what you know about compilers and computers, and it’s pretty easy to predict in every functional language I’ve seen, but all the contract really says is that the return value of your function will be correct.

    That gives the compiler a lot of room to be awesome, and a lot of room to suck. When the compiler’s chosen execution plan takes too long or requires too much space, that sucks. It failed to choose the correct HOW, so now you have to step in to provide it, sacrificing some of that sweet WHAT abstraction.

    So what I’m trying to say is that I agree with both of you. Yes, every time a functional language’s compiler executes our code less than optimally that is bad. As compiler authors and as application authors, we should try to avoid it. And yes, functional programming is a useful abstraction because it gets us closer to WHAT in a lot of cases that matter. But I don’t think purity is the end; the fact that its performance falls short in situations people run into every day is a strike against it.

    And of course, none of this changes the fact that you should always be using the right tool for the job. :)

Comments are closed.