Functional programming has gained quite some popularity in recent years. Yet if you code with the Julia language you probably already used a lot of functional programming concepts without really thinking about it. In it's essence functional programming simply means that functions can be used as arguments in other functions.

I noticed recently that I have been using more functional programming concepts in my daily coding. Mostly I am moving away from vectorized code to using "higher order functions". This might sound fancy, but it's pretty straightforward. Let me explain with some examples.

Here are a few simple ways to check whether an array has any values less than 3.

```
numbers = [1, 2, 3, 4, 5]
# vectorized
any(numbers .< 3)
# functional, using a pre-defined function
less_than_three(x) = x < 3
any(less_than_three, numbers)
# functional, using a lambda/anonymous function
any(x -> x < 3, numbers)
# functional, using a 'currying' function
any(<(3), numbers)
```

You can see that the `any`

function can either take a single (boolean) vector as input, or it can take a function and a vector as input. Passing a function into a function is a form of *functional* programming. Such functions that use functions are called *higher order functions.*

You can input any kind of function into `any`

that returns a boolean. It can just be the name of an existing function, an "anonymous" function like `x -> x < 3`

or as shown above a "curried" function. The functional programming people love inventing new names for concepts. Currying just means that a function can return a function with some arguments already filled in. So `f(1, 2, 3)`

can become `f(1)(2)(3)`

. This is what happened with the function call `<(3)`

, it will return a function similar to `x -> x < 3`

. Essentially you called something like `<(y) = x -> x < y`

, so all of these examples are equivalent:

```
julia> 2 < 3
true
julia> <(2,3)
true
julia <(3)(2)
true
```

I nowadays always choose the functional style of programming like `any(<(3), numbers)`

, since the vectorized form will first allocate the boolean vector `numbers .< 3`

in memory before calling `any`

. The functional form does not need to create this vector in memory. So the functional style is typically more performant, especially if the `any`

function can stop early:

```
julia> using BenchmarkTools
julia> numbers = 5 .* ones(10_000);
julia> @btime any($numbers .< 3);
4.271 μs (3 allocations: 5.55 KiB)
julia> @btime any(<(3), $numbers);
3.750 μs (0 allocations: 0 bytes)
julia> numbers[5] = 0.0;
julia> @btime any($numbers .< 3);
4.229 μs (3 allocations: 5.55 KiB)
julia> @btime any(<(3), $numbers);
3.400 ns (0 allocations: 0 bytes)
```

Next to `any`

I mostly use `all`

, `filter`

(and `filter!`

), `map`

, `reduce`

and `mapreduce`

in my daily coding. The functions `any`

, `all`

and `filter`

seem obvious in their behavior:

```
julia> numbers = [1, 2, 3, 4, 5];
julia> any(<(3), numbers)
true
julia> all(isequal(3), numbers)
false
julia> filter(<(3), numbers)
2-element Vector{Int64}:
1
2
```

The `map`

function is typically similar to a simple broadcast, it just applies (in other word *maps*) a function to each element in a collection. `map(f, x)`

is equivalent to `f.(x)`

in many cases, so you can choose whichever you like:

```
julia> numbers = [1, 2, 3];
julia> map(x -> x^2, numbers)
5-element Vector{Int64}:
1
4
9
julia> numbers.^2
5-element Vector{Int64}:
1
4
9
```

However, in some cases `map`

is more efficient, see this discussion here.

What I find more interesting are `reduce`

and `mapreduce`

. The `reduce`

function essentially applies a function iteratively to two subsequent elements in a collection. I think a simple example is more clear:

```
julia> numbers = [1, 2, 3, 4, 5];
julia> reduce(+, numbers)
15
julia> sum(numbers)
15
```

More powerful is the `mapreduce`

function, which as the name suggests, combines both a map and a reduce:

```
julia> numbers = [1, 2, 3, 4, 5];
julia> mapreduce(x -> x^2, +, numbers)
55
julia> sum(numbers.^2)
55
```

As before the broadcast/vectorized code will create another vector in memory (the `numbers.^2`

) and only then does the summing, while the `mapreduce`

doesn't need to do this, so that's a big advantage for `mapreduce`

. To be fair, Julia also allows a mapreduce with `sum(x -> x^2, numbers)`

, which might be more readable in this case.

Wow, so we actually discussed a lot of functional programming concepts here, without going into the details:

higher order functions like

`any(f, collection)`

anonymous functions like

`x -> x^2`

curried functions like

`<(3)`

reduce functions like

`reduce`

and`mapreduce`

Another concept that functional programmers love, but which I barely use in Julia is *function composition*. Here's an example:

```
# let's say we have two functions
add_one(x) = x + 1
double(x) = 2x
# we can define our own compose function
compose(f,g) = x -> f(g(x))
add_one_and_double = compose(double, add_one)
add_one_and_double(5) # returns 12
# or using the compose operator ∘, which does the above
add_one_and_double = double ∘ add_one
add_one_and_double(5) # returns 12
```

It may look very elegant, but I only occasionally see a use for such composition. And it's unintuitive to many programmers unfamiliar to the concept. You can already compose functions the old-fashioned way: `add_one_and_double(x) = double(add_one(x))`

and that serves most purposes in my opinion.

So that's it! These are all functional programming concepts that I use on a daily basis in my Julia programming. Especially the use of "higher order functions", like `any(<(3), [1,2,3,4])`

I use a lot and actively try to favor over any vectorized broadcasting. If you've been coding in Julia for a while now, I bet you've secretly already been doing lots of functional programming.