What is the mathematically formal definition of a function?

Given sets $X, Y$ (referred to as the *domain* and *codomain*, respectively), a subset $f \subset X\times Y$ is a *function* if it satisfies the following two properties.

In words, *every admissible input has a well-defined output.*

In words, *each input can have at most* *one* *output.*

The first thing you usually learn about in functional programming is the notion of a *pure function*. What is a pure function? A pure function is, firstly, a function that conforms to the above definition. That is, a function which satisfies the two properties:

- Each admissible input has a well-defined output
- Each input has at most one output

It is relatively straightforward to construct a function which violates one of the above two properties.

```
module Main
( main, f
) where
main :: IO ()
f :: Int -> Int
f x = x `div` x
main = print (show (f 0)) -- divide by zero
```

The above function violates property 1. Zero is an admissible input with an ill-defined output.

`let f = a => Math.random()`

The above function violates property 2. It returns a different (with high probability) value for a fixed argument `a`

.

There are some additional points to be made about pure functions. Points that are taken for granted in the world of mathematics. (Note that the three examples below are in C).

A function as defined above should effectively be *blind* to any variables which are not explicitly passed to it as arguments or which have not been instantiated within its body. Obvious, right?

```
int a = 0;
int f() { return a; }
```

Maybe not so obvious…

The above function is permissible in many languages. What makes it impure?

The function is acting on a variable outside of the scope of its arguments and body. The function below would be a correction.

```
int f() {
int a = 0;
return a;
}
```

This is known as *referential transparency*.

```
int a = 0;
int f() {
a = 1;
return a;
}
```

The function above not only acts on a variable outside of its scope, it also *mutates* it. A pure function is not permitted to mutate the state of a variable outside of its scope.

In fact, even keeping variables inside the scope of a function does not in itself protect those variables from mutation. One may pass a variable *by reference* as an argument to the function, in which case it is *in-scope* and yet mutable by the function.

```
int f(int *a) {
*a = 1;
return *a;
}
int main() {
int a = 0;
f(&a);
printf("%d\n", a); // 1
return 0;
}
```

Here we are passing arguments to the function *by reference*, making the arguments mutable. So, as far as the function is concerned the variable is *in scope* yet mutable.

The takeaway here is *thou shalt not mutate variables* (arguments or otherwise) and *thou shalt not act on arguments not explicitly passed or internally instantiated.*

Below is a demonstration of what is arguably the simplest pure function, the identity function, in several languages.

```
f :: Int -> Int
f x = x
```

`def f(x: Int): Int = x`

```
fn f(x: i64) -> i64 {
return x;
}
```

`const f = x => x`

```
(defn f
[x]
x)
```

`f x = x`