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 0.6 introduced the new ∘ (\circ) function composition operator. It currcntly has one method:

which has a rather straightforward definition:

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

For example

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

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:

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

We can now define some simple functions very easily:

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

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:

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:

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:

Which can be used in the following way:

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

Which allows the following:

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[1] + x[2]
21

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

julia> next_x = [x[2], x[1] + x[2]]
[13,21]

Or equivalently, using our / operator to add l[1] and l[2]:

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

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

Or equivalently, using our ^ operator:

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

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 nth list instead of the second element of the n-1th list.