# Pure Functions - Practical Functional Programming | Part 2

Sandro Maglione

Get in touch with meFunctional programming

25 July 2022[updated]

•4 min read

Sandro Maglione

Functional programming

Pure functions are one of the core principles of Functional Programming. In this article, you are going to learn why using pure functions, what a pure function is, and how to implement pure functions in your code.

Practical Functional Programming step by step is a series in which we are going to uncover the principles of functional programming and effective coding one small piece at the time.

Starting from a complete code example, we are going to learn step by step what makes a good functional code and **what are the benefits of applying a functional programming paradigm** to your codebase.

## Review assignment: Find repeated characters

In the last post, we introduced the problem we have been assigned to solve, of course using functional programming. The problem statement is the following:

Given a`String`

, find all the characters that arerepeated 2 or more timesand return a new`String`

containing only these characters.

We then explored the solution to the assignment using functional programming in three different languages: Haskell, Typescript, and Dart.

Today we are going to introduce the first core principle which sits at the core of functional programming: **Pure Functions**.

## Why would we need Pure Functions

The first question starts always with why: why should I write Pure Functions?

Pure Functions are based on the mathematical definition of a function. In math, every function takes some inputs and **based only on these inputs** it produces an output. A mathematical function cannot access global variables or mutate any state outside itself.

This simple assumptions unlock some interesting considerations:

**Every function is self-contained**; it does not need to know anything about the outside world and it cannot modify it (immutability!)**Every function can be treated as a black box**; you can look at a function in isolation and understand it without knowing anything about the current global state**Same inputs, same output**; Since a Pure function has access only to its inputs and anything else, the output given the same inputs is always the same

We can see why Pure functions are beneficial in coding. Pure functions allow local reasoning. You can focus on one single function in isolation without the need to understand the global state of the application. Furthermore, since every function can be treated as a black box, often times the signature of a function is all you need to know to effectively use it.

## Pure functions in our assignment's solutions

All three of the solutions I proposed you in Part 1 are Pure functions. Let's explore the `buildmap`

function in dart as an example:

```
Map<String, int> buildmap(String str) => str.split('').foldLeft(
<String, int>{},
(acc, x) => {
...acc,
x: (acc[x] ?? 0) + 1,
},
);
```

```
Map<String, int> buildmap(String str) => str.split('').foldLeft(
<String, int>{},
(acc, x) => {
...acc,
x: (acc[x] ?? 0) + 1,
},
);
```

We are going to review the principles of Pure functions using this simple example:

- Self-contained:
`buildmap`

does not access any variable outside of the`str`

input. Moreover, the functions`split`

and`foldLeft`

do not modify the original variable (immutability) - Black box: Since the function is based only on its input, you can effectively forget about the implementation and use it without worrying about any side effect. Moreover, you do not need to know anything about the context of the application in which this function is used.
- Same input, same output: You can call the function an infinite number of times with the same input, you will get always the same output

The same considerations are valid also for the Typescript and Haskell solutions:

```
// Use the functions split, reduce, modifyAt, and upsertAt
// Output based only on the str input
const buildmap = (str: string): Map<string, number> =>
pipe(
str.split(''),
reduce(new Map<string, number>(), (acc, x) =>
pipe(
acc,
modifyAt(eqString)(x, (n) => n + 1),
O.getOrElse(() => pipe(acc, upsertAt(eqString)(x, 1)))
)
)
);
```

```
// Use the functions split, reduce, modifyAt, and upsertAt
// Output based only on the str input
const buildmap = (str: string): Map<string, number> =>
pipe(
str.split(''),
reduce(new Map<string, number>(), (acc, x) =>
pipe(
acc,
modifyAt(eqString)(x, (n) => n + 1),
O.getOrElse(() => pipe(acc, upsertAt(eqString)(x, 1)))
)
)
);
```

```
-- Use functions foldl, member, adjust, and insert
buildmap :: String -> Map Char Int
buildmap =
foldl
( \acc x ->
if member x acc
then adjust (+ 1) x acc
else insert x 1 acc
)
empty
```

```
-- Use functions foldl, member, adjust, and insert
buildmap :: String -> Map Char Int
buildmap =
foldl
( \acc x ->
if member x acc
then adjust (+ 1) x acc
else insert x 1 acc
)
empty
```

Remember, these three functions are exactly the same, even if they are written in different languages:

`foldLeft`

(dart),`reduce`

(typescript),`foldl`

(haskell)`split`

(dart, typescript)`modifyAt`

(typescript),`adjust`

(haskell)`upsertAt`

(typescript),`insert`

(haskell)

Hopefully you now have a more clear idea of what a Pure function is and also **why it is useful to write Pure functions**. We are going to move one principle at the time. That's it for this Part 2.

See you soon for Part 3!