liveBook · Manning

Pure function

As explained in The Well-Grounded Rubyist, Fourth Edition

A pure function is a computation that produces its result from its inputs without observable side effects or dependence on mutable external state.

A pure function returns a result determined solely by its inputs and has no observable side effects. Give it the same inputs again, and it returns the same result without changing anything else. This makes its calls predictable: a pure expression is referentially transparent when replacing it with its value leaves the program’s behavior unchanged. section 16.1 section 16.1.2

Sum without consuming the array

Consider `arr = [1, 3, 5]`. A method that sums by repeatedly calling `arr.shift` returns 9, but empties the caller’s array; a second call returns 0. Defining the method as `def sum_of_parts(arr); arr.reduce(:+); end` instead returns the sum without modifying `arr`, so the same array can be used again. section 16.1.1

Ruby methods can express this idea without special function syntax. `3 + 4` is a call to `3.+(4)`: with the same receiver and argument, it returns 7 without modifying either. Likewise, `map` produces a new array rather than changing its receiver. When reasoning about a call that takes a block, consider the block’s behavior too. section 16.1.2

Look for effects beyond the return value:

  • Mutation: `upcase` leaves a string alone, while `upcase!` changes it; appending to an array and deleting from a hash also change state. section 16.1.3
  • External effects: terminal output, database changes, and web-page updates are observable side effects. An unhandled exception is also treated as a side effect here. section 16.1.1 section 16.1.3

Removing stored state can move a design toward pure functions, but it does not make every method pure. A grading calculation can take scores and return a letter instead of storing the letter in an instance variable. Issuing the report still prints output. Keep such effects where they are needed rather than treating all state changes as mistakes. section 16.1.4 section 16.1.3

How The Well-Grounded Rubyist, Fourth Edition covers it

How deep it goes
Defines purity and referential transparency, then works through mutating and nonmutating Ruby methods; does not pursue the underlying mathematics deeply.
Examples
Contrasts array summation with `shift` and `reduce`, shows arithmetic and `map`, and refactors a grading calculation.
What it assumes
Assumes familiarity with Ruby methods, receivers, blocks, and arrays while introducing functional-programming principles through small examples.
What you can do after
Diagnose/debug with it by checking whether a Ruby method’s result depends only on inputs and whether it changes observable state.
How it connects
Connects pure functions to referential transparency, side effects, immutability, object state, and functional programming.

Also explained in

Compare how 19 books explain pure function →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of The Well-Grounded Rubyist, Fourth Edition. The section links point to the parts of the book it draws on.