Julia is a relatively new programming language. Let’s see what it can offer to Python developers.
Why should you care? Julia has a lot of benefits:
- It's fast (like C or C++, and unlike Python or Ruby)
- It's easy to learn and quick to prototype with (like Python, and unlike C, C++, Java)
- It's free (basically like every language save for Matlab or Mathematica)
Julia was designed to perform high-performance numerical and scientific computing.
It's fast thanks to the JIT (just-in-time) compiler and a smart typing system that helps the compiler to optimize the code. To make the most of it, developers need to know how to use the typing system properly. But once you get the hang of it, you’re bound to see some spectacular results. On top of that, Julia offers powerful metaprogramming capabilities.
In this presentation, I plan to focus on the following aspects of Julia:
- Basic types
- Defining functions
- Control flow
Before we start, let's see JIT compiler in action:
As you can see, some calls to add are significantly faster and require much less memory than the others. It’s because Julia’s compiler specialises the function’s compiled code on the basis of its first usage for given argument-types. So after it’s first called with two integers, every other call with any integers will be way faster (and will take less memory). To make the function fast for two floats we just need to call it with any two floats and the compiler will specialise the compiled function for those types too. It works the same way for other types (and type combinations) as well.
Let’s move to the overview of the Julia aspects I mentioned earlier.
1.1. Numeric types
In Julia, we can easily create an integer or a float:
No surprise here. On 64-bit systems integers and floats have 64 bits by default.
What about rational numbers? In Julia there is a special operator to create them, double slash: //. The syntax it introduces is pretty nice and readable:
In Python there's no special operator for rational numbers. Here's what the code would look like in Python:
We first need to import fraction, then create a fraction object. It works the same, but Julia's // operator is just much simpler and natural. Moreover, in Python we can't set the denominator to zero (Python treats it as trying to divide by zero). It’s also worth mentioning that in Python 3 // is a floor division operator. Its result is quotient without remainder.
1.2. Strings and Chars
Let's move on now to other basic types – strings and chars (characters).
Strings are really simple in Julia, just like in Python:
Unlike in Python, indexing strings in Julia returns chars:
Whereas in Python with indexing a string a sub-string of a single letter gets returned:
In fact, in Python there is no char type.
Let’s look at some basics of Julia’s syntax.
Julia uses a 1-based indexing. That's of the biggest syntactic differences between Julia and Python – or actually, between Julia and the majority of modern programming languages. Note: Matlab, Mathematica, and Fortran use 1-based indexing as well.
To access the last element use end keyword (unlike in Python, where index of -1 is used):
To get slices of strings we can use notation similar to Python’s one – [start:stop]:
So in the first example we get first three letters and in the second – letters from second to the last but one, etc. We can also use the [start:step:stop] syntax to define the range of slicing with a step:
In this case it differs from Python, which uses [start:stop:step] notation ("stop" and "step" are swapped).
As already mentioned, 1-based indexing distinguishes Julia form pretty much all other modern programming languages. That can be irritating, especially when you are constantly switching between Julia and, say, Python. Matlab programmers won’t have this problem, though ;)
2.2.1. Defining functions
We've already seen that one in action. Let’s look at it more closely. Its basic structure is the following: function keyword, function's name, parameter(s) in brackets, function's body and end keyword:
function identity(x) return x end function sum_of_three(a, b, c) return a + b + c end
Unlike in Python, the return keyword is optional and the last expression gets returned. We can use return to return immediately – for example, in an if condition or in a loop when we want to stop it and return immediately. Indentation is optional as well – the end keyword defines the end of blocks. So this is equivalent:
function identity(x) x end function sum_of_three(a, b, c) a + b + c end
It's not recommended to format your code like this, though. Indentation helps to make your code nice and readable. If you really want to define simple functions in one line, use the following notation:
In Julia we can define functions also using a notation similar to mathematical one:
identity(x) = x sum_of_three(a, b, c) = a + b + c
This is pretty handy is cases of simple one-liners.
Using anonymous functions (lambdas)
In Julia we can also use anonymous functions – here's an example:
# identity function, without a name x -> x \# adding three items function, without a name (a, b, c) -> a + b + c
We can bind an anonymous function to a name:
# identity function, bound to \`identity\` name identity = x -> x \# adding three items function, bound to \`sum_of_three\` name sum_of_three = (a, b, c) -> a + b + c
In Julia functions (just like in Python) are first-class objects, so binding lambdas to names is really the same as binding a number or a string.
I show this to present Julia's capabilities, if you want to define one-lined function just use the math-like notation. Lamdbas are better suited for defining functions directly in a call to another function, and similar tasks (see next section).
Let's check if all the variants of defining functions actually work:
2.2.2. Operators as functions
An interesting thing in Julia that Python developers may find surprising is that the plus is used as an infix operator, like in 1 + 2, is in fact a syntactic sugar for normal “prefix” function call: +(1, 2):
So we can pass operators around just like other functions. Here's an example:
2.2.3. Function naming conventions
Julia has a smart naming convention: if a function's name ends with a bang (!), it may mutate its arguments. Here's what it looks like in practice:
I used Array type there, see docs.
The reverse function returns the reversed Array and does not change the original one. The reverse! function returns reversed Array and modifies original one at the same time. That might seem like a small detail, but it's really helpful.
2.3. Control flow
Let's see basic control flow structures in Julia.
2.3.1. Conditional syntax
Here's how you use conditions in Julia:
(Using $ in a string is Julia's string interpolation syntax. For more on this see the docs.)
An important difference between Julia and Python is that in Julia if-elseif-else-end blocks are expressions (they return a value), not statements. "This value is simply the return value of the last executed statement in the branch that was chosen" [docs].
So here's an example that shows this:
In both cases the last statement of chosen branch is returned and bound to answer.
2.3.2 For loops
Here's how we define for loops in Julia:
1:6 is a range object that generate values (somewhat like in Python):
BONUS 1: Calling Python
What if we wanted to use Python's requests library in Julia?
In Python, using the library would look like this:
In Julia, we can use the PyCall library.
The PyCall imports the request library and allows us to use it.
BONUS 2: Special characters in code
We can easily use LaTeX syntax to get special symbols and use them as names, like:
\phi - ϕ
\Sigma - Σ
\Delta - Δ
\pi - π
\equiv - ≡
Just type back-slash (\), then symbol's name and press Tab.
We can use these symbols as names as well:
Julia is a general purpose language, but it was specifically designed for numerical and scientific computing. Thanks to JIT it’s very fast, it’s type system and syntax is tailored scientific purposes. It even allows using special symbols as names in the code, which is very handy in the scientific context.
Julia is very easy to pick up by Python and Matlab programmers. Generally, it has a very gentle learning curve, so it’s accessible even to people who don’t know much about programming.
So Julia is on its way to becoming a serious competitor for Python regarding numerical and scientific computing. That competition is only going to get more interesting as Julia’s ecosystem grows stronger.
You can find all the code I've used here.
Have you got any questions about Julia? Leave me a comment, I'm always happy to share my experience with developers taking their first steps in Julia or Python.