# Straightforward Functional Programming Examples in Julia

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.

```julia
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
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
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
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
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](https://discourse.julialang.org/t/when-to-use-broadcasting-with-vs-map/58078).

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
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
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:

```julia
# 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.
