Jump to content

Pure function

From Wikipedia, the free encyclopedia

This is an old revision of this page, as edited by AnomieBOT (talk | contribs) at 05:03, 9 November 2022 (Dating maintenance tags: {{Clarify}}). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

In computer programming, a pure function is a function that has the following properties:[1][2]

  1. the function return values are identical for identical arguments (no variation with local static variables, non-local variables, mutable reference arguments or input streams), and
  2. the function has no side effects (no mutation of local static variables, non-local variables, mutable reference arguments or input/output streams).

Thus a pure function is a computational analogue of a mathematical function. Some authors, particularly from the imperative language community, use the term "pure" for all functions that just have the above property 2[3][4] (discussed below).

Examples

Pure functions

The following examples of C++ functions are pure:

  • floor, returning the floor of a number;
  • max, returning the maximum of two values.
  • the function f, defined as
    void f() {
      static std::atomic<unsigned int> x = 0;
      ++x;
    }
    
    The value of x can be only observed inside other invocations of f(), and as f() does not communicate the value of x to its environment, it is indistinguishable from function void f() {} that does nothing. Note that x is std::atomic so that modifications from multiple threads executing f() concurrently do not result in a data race, which has undefined behavior in C and C++.

Impure functions

The following C++ functions are impure as they lack the above property 1:

  • because of return value variation with a static variable
    int f() {
      static int x = 0;
      ++x;
      return x;
    }
    
  • because of return value variation with a non-local variable
    int f() {
      return x;
    }
    
    For the same reason, e.g. the C++ library function sin() is not pure, since its result depends on the IEEE rounding mode which can be changed at runtime.
  • because of return value variation with a mutable reference argument
    int f(int* x) {
      return *x;
    }
    
  • because of return value variation with an input stream
    int f() {
      int x = 0;
      std::cin >> x;
      return x;
    }
    

The following C++ functions are impure as they lack the above property 2:

  • because of mutation of a local static variable
    void f() {
      static int x = 0;
      ++x;
    }
    
  • because of mutation of a non-local variable
    void f() {
      ++x;
    }
    
  • because of mutation of a mutable reference argument
    void f(int* x) {
      ++*x;
    }
    
  • because of mutation of an output stream
    void f() {
      std::cout << "Hello, world!" << std::endl;
    }
    

The following C++ functions are impure as they lack both the above properties 1 and 2:

  • because of return value variation with a local static variable and mutation of a local static variable
    int f() {
      static int x = 0;
      ++x;
      return x;
    }
    
  • because of return value variation with an input stream and mutation of an input stream
    int f() {
      int x = 0;
      std::cin >> x;
      return x;
    }
    

I/O in pure functions

I/O is inherently impure: input operations undermine referential transparency, and output operations create side effects. Nevertheless, there is a sense in which function can perform input or output and still be pure, if the sequence of operations on the relevant I/O devices is modeled explicitly as both an argument and a result, and I/O operations are taken to fail when the input sequence does not describe the operations actually taken since the program began execution.[clarification needed]

The second point ensures that the only sequence usable as an argument must change with each I/O action; the first allows different calls to an I/O-performing function to return different results on account of the sequence arguments having changed.[5][6]

The I/O monad is a programming idiom typically used to perform I/O in pure functional languages.

Compiler optimizations

Functions that have just the above property 2 allow for compiler optimization techniques such as common subexpression elimination and loop optimization similar to arithmetic operators.[3] A C++ example is the length method, returning the size of a string, which depends on the memory contents where the string points to, therefore lacking the above property 1. Nevertheless, in a single-threaded environment, the following C++ code

std::string s = "Hello, world!";
int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int l = 0;

for (int i = 0; i < 10; ++i) {
  l += s.length() + a[i];
}

can be optimized such that the value of s.length() is computed only once, before the loop.

Some programming languages allow for declaring a pure property to a function:

  • In Fortran and D, the pure keyword can be used to declare a function to be just side-effect free (i.e. have just the above property 2).[7] The compiler may be able to deduce property 1 on top of the declaration.[8]
  • In the GCC, the pure attribute specifies property 2, while the const attribute specifies a truly pure function with both properties.[9]
  • Languages offering compile-time function execution may require functions to be pure, sometimes with the addition of some other constraints. Examples include constexpr of C++ (both properties).[10]

Unit testing

Since pure functions have identical return values for identical arguments, they are well suited to unit testing.

See also

References

  1. ^ Bartosz Milewski (2013). "Basics of Haskell". School of Haskell. FP Complete. Archived from the original on 2016-10-27. Retrieved 2018-07-13. Here are the fundamental properties of a pure function: 1. A function returns exactly the same result every time it's called with the same set of arguments. In other words a function has no state, nor can it access any external state. Every time you call it, it behaves like a newborn baby with blank memory and no knowledge of the external world. 2. A function has no side effects. Calling a function once is the same as calling it twice and discarding the result of the first call.
  2. ^ Brian Lonsdorf (2015). "Professor Frisby's Mostly Adequate Guide to Functional Programming". GitHub. Retrieved 2020-03-20. A pure function is a function that, given the same input, will always return the same output and does not have any observable side effect.
  3. ^ a b "GCC 8.1 Manual". GCC, the GNU Compiler Collection. Free Software Foundation, Inc. 2018. Retrieved 2018-06-28.
  4. ^ Fortran 95 language features#Pure Procedures
  5. ^ Peyton Jones, Simon L. (2003). Haskell 98 Language and Libraries: The Revised Report (PDF). Cambridge, United Kingdom: Cambridge University Press. p. 95. ISBN 0-521 826144. Retrieved 17 July 2014.
  6. ^ Hanus, Michael. "Curry: An Integrated Functional Logic Language" (PDF). www-ps.informatik.uni-kiel.de. Institut für Informatik, Christian-Albrechts-Universität zu Kiel. p. 33. Archived from the original (PDF) on 25 July 2014. Retrieved 17 July 2014.
  7. ^ Pure attribute in Fortran
  8. ^ Pure attribute in D language
  9. ^ "Common Function Attributes". Using the GNU Compiler Collection (GCC. Retrieved 22 July 2021.
  10. ^ constexpr attribute in C++