DEV Community

Discussion on: Learning Functional Programming

Collapse
 
kspeakman profile image
Kasey Speakman • Edited

The two conditions you need for a function to be pure.

  1. The return value of a function depends only on the input parameters.
  2. External values cannot be mutated (input values, globals, etc.).

Example violations.

let mutable x = 1

let addX value =
    value + x // violates #1

// x is not an input parameter.
// probably would still be ok if x was immutable.
type MyType = { mutable AnInt : int } 
let add x myType =
    // violates #2, mutates the input
    myType.AnInt <- myType.AnInt + x

Mutations to shared data are what makes code hard to follow and hard to fit in your head. This is sometimes called hard to "reason about".

The challenge of FP is figuring out how to get all significant decisions into pure functions. That way they are easy to test and reason about. Then push all side effects out to the impure edges of the application.

Thread Thread
 
idanarye profile image
Idan Arye

The input values cannot be mutated.

Just "the input values"?

Thread Thread
 
kspeakman profile image
Kasey Speakman • Edited

Yep. Technically, a mutation of a local, private variable (i.e. not passed by reference in or out), or using a out-of-scope (e.g. global) variable which doesn't change during program execution... both still allow a function to be pure. Such side effects do not affect the caller or any callees.

However, these are not great habits for FP learners to start with. I tend to only do local mutation for performance optimizations. Most of the time they aren't needed.

Thread Thread
 
idanarye profile image
Idan Arye

"The input values cannot be mutated" implies that global or closure variables can be mutated.

Thread Thread
 
kspeakman profile image
Kasey Speakman

Nice catch. I revised the wording.

Thread Thread
 
vasilvestre profile image
Valentin Silvestre

It seem to me ok but.. really hard and annoying.
Why do FP need pure function ?

Thread Thread
 
kspeakman profile image
Kasey Speakman

Modeling with pure functions is difficult at first, particularly if you know imperative programming well. In fact it took me a while to get used to it. But it eventually becomes familiar, and not any harder to do than procedural or OOP. Then you start to wish you had discovered it a lot sooner.

The payoff is well worth it. Primarily because pure functions have linear risk/cost to refactor. I don't know about you, but my experience previously has been that refactors to core code can have an unknown risk of breaking other things... but not so with pure functions. When you look at a function, what you see is what you get... no mysterious external, runtime state changes to account for. Refactorability is a benefit that pays dividends for many years to come. And as I said, once it becomes familiar its not even any more work (and perhaps less) than other methods. The main trade-off is the upfront cost of learning it.

Pure functions are also quite easy to test. If all your significant decisions are in pure functions, you don't really need mocks, fakes, and extensive test frameworks. You just need to vary the inputs and check the outputs. Testing logic is low-cost and easy. Testing side effects becomes integration testing (part of CI workflow, not in unit tests).