add pure-functions/index.md (#32887)

A description of the important of pure functions and their limitations in Erlang
This commit is contained in:
Paniscus Ateles
2019-07-19 22:30:26 +01:00
committed by Quincy Larson
parent d44441d6cb
commit a8e05e25a3

View File

@ -0,0 +1,102 @@
---
title: Pure Functions
---
## Pure Functions
The concept of 'pure functions' is not difficult to grasp but it is more difficult to grasp just how important
the concept is to functional programming and how programmers reason about code.
#### More Information:
<!-- Please add any articles you think might be helpful to read before writing the article -->
* [Wikipedia: Pure functions](https://en.wikipedia.org/wiki/Pure_function)
* [Learn You Some Erlang: Errors and Exceptions](https://learnyousomeerlang.com/errors-and-exceptions)
### What is a Pure Function ?
Think of a subroutine that returns no value at all.
Well, if it serves any useful purpose, it must do so by side-effects:
it may change global data, it may be a method changing the state of an object, it may do I/O.
This is technically a (pure) procedure.
Pure procedures are rare as usually at least a code indicating success / failure is returned.
Consider a subroutine that returns a value and has no side-effects (e.g. square_of/1).
This is a pure function.
Most programming languages allow a subroutine to be both a procedure with side-effects and a function with a return value.
In functional programming languages, functions are pure (or tend to be) and there are no procedures.
What are the consequences ?
### Consequences of using Pure Functions
A pure function, given the same input, will always produce the same result.
This offers compilers more scope for optimisation.
Compilers can examine an entire program to deduce which functions are pure and which are not.
A programmer cannot do this for a program of any size (even one they wrote themselves).
They can study a subroutine and say "assuming everything else stays the same, it does this" but often checking
that everything else stays the same is a lot more effort than studying the subroutine.
In a language where all functions are pure, the assumption is guaranteed.
This simplifies greatly the task of maintaining large applications:
you can change an individual function with confidence that there will be no unintended side-effects.
### Limitations of a Pragmatic Functional Programming Language
While most functions in Erlang are pure, there are certain features of the language that produce impure functions with side effects:
* functions may do I/O - I/O has side effects,
* actors send messages to one another - see the concurrent half of Erlang,
* functions may throw exceptions.
The first two are generally confined to small sections of code so the impurity is not widespread:
printing out some values for debug or logging purposes in one routine
does not suddenly mean all the functions around it are under serious threat from side-effects.
Throwing exceptions is a very bad idea.
It is a double-edged side-effect often there to force some function to clean up another function's run-time mess.
The Erlang philosophy is "Let it crash" so, in general Erlang programmers ignore this side-effect too.
The "Let it crash" philosophy is controversial.
In large, concurrent, Erlang applications, the view is that high reliability is achieved by
allowing the individual parts of the application to crash and be replaced without stopping the application.
By failing early, rather than trying to carry on,
the failed part is less likely to compromise the application or its data.
Developers can examine the crash and even load a fix without stopping the application.
Throwing an exception becomes a flag that says "If you want to get to the bottom of this bug, dig here".
### How to return errors from functions
Pure functions are all very well but what happens when the arguments to a function means the function cannot determine the result ?
When it is known that this will happen and the caller may reasonably be expected to go to Plan B,
it makes sense for the function to return a 'return code' as well as the 'return value'.
This can be done using a tuple:
```Erlang
% in the called function ...
if Y /= 0 -> {ok, X / Y};
y == 0 -> {err, 0}.
```
and:
```Erlang
% in the caller ...
case safe_divide(X,Y) of
{ok, Result} -> format("That worked");
{_, _} -> format("Oops, again");
end
```
<!-- EOF -->