# Operator Flexibility in Julia (AKA Abusing Operators in Julia)

Believe it or not but the following is actual Julia code which generates Fibonacci numbers:

``````julia> (n->((l->[l,l/+])^n)(0:1)).(1:10)
10-element Array{Int64,1}:
1
1
2
3
5
8
13
21
34
55``````

Julia 0.6 introduced the new ∘ (\circ) function composition operator. It currcntly has one method:

``````julia> methods(∘)
# 1 method for generic function "∘":
∘(f, g) in Base at operators.jl:884``````

which has a rather straightforward definition:

``∘(f,g) = (x...)->f(g(x...))``

It takes functions `f` and `g` and composes them in the following way:

``(f∘g)(x) == (f(g(x)))``

For example

``````julia> sin(cos(2))
-0.4042391538522658

julia> (sin∘cos)(2)
-0.4042391538522658

julia> (sin∘cos∘log)(2)
0.6955886362231636

julia> sin(cos(log(2)))
0.6955886362231636``````

The operator provides a nice way of creating anonymous functions for broadcasting across arrays:

``````julia> map(reverse∘string, 230:240)
11-element Array{String,1}:
"032"
"132"
"232"
"332"
"432"
"532"
"632"
"732"
"832"
"932"
"042"``````

Ultimately, the ∘ operator is a great demonstration of Julia’s flexibility as a language. Which leads to the question: What else can we do with Julia’s operators?

For starters, it may be nice to be able to compose functions and arguments so that `(arg∘f)(x)` is equivalent to `f(arg,x)`. For example, the following does not work:

``````julia> map(10∘(-), 1:3)
ERROR: MethodError: objects of type Int64 are not callable
Stacktrace:
 _collect(::UnitRange{Int64}, ::Base.Generator{UnitRange{Int64},Base.##55#56{Int64,Base.#-}}, ::Base.EltypeUnknown, ::Base.HasShape) at ./array.jl:454
 map(::Function, ::UnitRange{Int64}) at ./abstractarray.jl:1865``````

However, thanks to Julia’s overloading abilities, we can make it work ourselves. Here we define both `arg∘f` and `f∘arg`:

``````julia> import Base.∘

julia> (∘)(f::Function, arg) = (x...)->f(x...,arg)
∘ (generic function with 2 methods)

julia> (∘)(arg, f::Function) = (x...)->f(arg,x...)
∘ (generic function with 3 methods)``````
``````julia> map(10∘(-), 1:3)
3-element Array{Int64,1}:
9
8
7

julia> map((-)∘10, 1:3)
3-element Array{Int64,1}:
-9
-8
-7``````

We can now define some simple functions very easily:

``````julia> my_log2 = 2∘log
(::#7) (generic function with 1 method)

julia> my_log2(10)
3.3219280948873626

julia> log2(10) # Julia built-in
3.321928094887362``````

This is great but we created an additional problem. Our old code no longer works!

``````julia> (sin∘cos)(3)
ERROR: MethodError: ∘(::Base.#sin, ::Base.#cos) is ambiguous. Candidates:
∘(arg, f::Function) in Main at REPL:1
∘(f::Function, arg) in Main at REPL:1
Possible fix, define
∘(::Function, ::Function)``````

Julia doesn’t know which of our functions to call. Since both `arg` and `f` are of type `Function` (`typeof(sin) <: Funtion == true` and `typeof(cos) <: Function == true`), it doesn’t know which to use as `f::Function` and which to use as `args::Any`. In fact, we don’t want it to use either, we want it to use the original definition if both arguments are functions. Luckily, this is a very easy fix in Julia. We can define a specialized version of the original definition like so:

``````julia> ∘(f::Function, g::Function) = (x...)->f(g(x...))
∘ (generic function with 4 methods)``````

Notice that this definition is exactly the same as the one above except that it includes specific types for its arguments `f` and `g`. Julia is now able to choose the proper function:

``````julia> (sin∘cos)(3)
-0.8360218615377305``````

Another way we can abuse- ahem demonstrate Julia’s flexibility with operators is to use them to perform common complex operations, for example, here we define the `/` operator to run `fold` on a single dimensional list:

``````julia> import Base./

julia> /{T}(l::AbstractArray{T,1}, f::Function) = foldr(f,l)
/ (generic function with 74 methods)``````

Which can be used in the following way:

``````julia> foldr(+, [1,2,3])
6

julia> [1,2,3]/+
6``````

Or perhaps we want the `^` operator to repeat a function:

``````julia> import Base.^

julia> function ^(f::Function, iters::Int)
fn = f
for i in 1:(iters-1)
fn = fn ∘ f
end
fn
end
^ (generic function with 53 methods)``````

Which allows the following:

``````julia> sin(sin(2))
0.7890723435728884

julia> (sin^2)(2)
0.7890723435728884``````

Gauss would be proud.

## Generating Fibonacci Numbers

Lets use our new operators to generate Fibonacci numbers. The first few terms of this sequence are the following:

``0,1,1,2,3,5,8,13,21,34,55,...``

The recursive definition for this sequence is:

fibn = fibn-1 + fibn-2

Lets say we have an array containing two adjacent numbers in the sequence:

``julia> x = [8,13]``

In order to generate the next Fibonacci number, we simply add the two items together.

``````julia> x + x
21``````

If we want to continue generating the next Fibonacci number, we will also need to save the previous one:

``````julia> next_x = [x, x + x]
[13,21]``````

Or equivalently, using our `/` operator to add `l` and `l`:

``````julia> next_x = [x, x/+]
2-element Array{Int64,1}:
13
21``````

Lets define a function so we can easily generate Fibonacci numbers:

``````julia> next_fib(x) = [x, x/+]
next_fib (generic function with 1 method)

julia> x = next_fib([8,13])
2-element Array{Int64,1}:
13
21

julia> x = next_fib(x)
2-element Array{Int64,1}:
21
34

julia> x = next_fib(x)
2-element Array{Int64,1}:
34
55``````

We can generate the `n`th Fibonacci number if we start at the first two Fibonacci numbers `[0,1]` and run the function `n-1` times:

``````julia> function fib(n)
x = [0,1]
for i in 1:n-1
x = next_fib(x)
end
return x
end
fib (generic function with 1 method)

julia> fib(10)
55``````

Or equivalently, using our `^` operator:

``````julia> fib(n) = (next_fib^(n-1))([0,1])
fib (generic function with 1 method)

julia> fib(10)
55``````

Now we can swap out `next_fib` for it’s definition:

``````julia> fib(n) = ((l -> [l, l/+])^(n-1))([0, 1])
fib (generic function with 1 method)``````

And finally, to make the code a little more concise (why not?) we use `0:1` instead of `[0,1]` and index the first element of the `n`th list instead of the second element of the `n-1`th list.

``````julia> fib(n) = ((l->[l,l/+])^n)(0:1)
fib (generic function with 1 method)

julia> fib(10)
55

julia> fib.(1:10)
10-element Array{Int64,1}:
1
1
2
3
5
8
13
21
34
55``````