In a nutshell, Julia truly is a new breed of programming language that successfully manages to combine the high performance of compiled languages with the agility of the dynamic ones, through a friendly syntax that feels natural and intuitive right from the start. Julia is fast (programs are compiled at runtime to efficient native code for multiple platforms), general (the standard library supports, out of the box, powerful programming tasks including asynchronous I/O, process control, parallel, and distributed computing, logging, profiling, package management, and more), dynamic and optionally typed (it is dynamically-typed with optional type declarations and comes with a powerful read-eval-print loop (REPL) for interactive and exploratory coding). It is also technical (excelling at numerical computing) and composable (thanks to its rich ecosystem of packages that are designed to work together seamlessly and with high performance).
Although initially it focused on addressing the needs of high-performance numerical analysis and computational science, recent releases have positioned the language in the area of general computing, with many classes of specialized functions being moved out of the core into dedicated modules. As such, it is also a great fit for client and server-side programming, due to its powerful capabilities for concurrent, parallel, and distributed computing.
Julia implements a type system based on parametric polymorphism and multiple dispatch, it is garbage-collected, uses eager evaluation, packs a powerful regular expression engine, and can call C and Fortran functions without glue code.
Let's take a look at the most important features of the language, the parts that make Julia stand out. If you're considering Julia for your next project, you can use this as a quick checklist against your requirements.
The key to Julia's performance is the combination between the LLVM-based just-in-time (JIT) compiler and a series of strategic design decisions that allow the compiler to generate code that approaches, and most of the times matches, the performance of C.
quicksort and a few others. They are designed to evaluate compiler performance against common code patterns such as function calls, string parsing, sorting, iterations, recursion, and more. There is a plot of the benchmarks, available at https://julialang.org/benchmarks/, which illustrates Julia's consistent top performance across all of the tests. The following plot depicts this:
The creators of Julia have carefully picked the most successful elements of syntax from other languages, with the goal of producing expressive, concise, and readable code. Julia provides powerful and expressive language constructs for high-level numerical computing, in the same way as languages such as R, MATLAB, and Python do. It builds upon the experience brought by existing mathematical programming languages but also borrows much from popular dynamic ones, such as Lisp, Perl, Python, Lua, and Ruby.
To give you a quick taste of idiomatic Julia, here's how to open a file, read it, output it, and then have the file automatically closed by Julia:
open(".viminfo") do io read(io, String) |> println end
In the preceding snippet, we open the
.viminfo file for reading passing
IOStream instance, into the underlying code block. The stream is then read into a
String that is finally displayed onto the console by piping it into the
println function. The code is very readable and easy to understand if you have some coding experience, even if this is your first time looking at Julia code.
do syntax (named after the
do part following the
open function) is inspired by Ruby's blocks—and it is, in fact, syntactic sugar for passing anonymous functions as method arguments. It is efficiently used in the preceding example to succinctly express a powerful design pattern for safely handling files, guaranteeing that the resources are not accidentally left open.
This goes to show the amount of attention that was put by the designers of the language to make Julia safe, beginner-friendly, expressive, concise, readable, and intuitive.
Julia's type system is a key feature of the language and one that has a major impact on both its performance and productivity. The type system is dynamic and optional, meaning that the developer can, but is not required to, provide type information to the compiler. If not provided, Julia will perform type inference, which is the process of deducing the types of later values from the types of input values. This is a very powerful technique, as it frees the programmers from having to worry about types, allowing them to focus on the application logic and making for a gentler learning curve. This is especially useful for prototyping and exploratory programming, when the complete set of constraints and requirements is not known beforehand.
However, understanding and correctly using the type system offers important performance benefits. Julia allows optionally adding type information, making it possible to indicate that a certain value must be of a specific kind. This is one of the cornerstones of the language, allowing performant method dispatching and facilitating the automatic generation of efficient, specialized code for different argument types. The type system allows the definition of rich type hierarchies, with user-defined types as fast and compact as the built-in ones.
If the languages of the 70s and 80s were designed under the strict requirements imposed by the limited CPU and RAM resources, the ones in the 90s and the 2000s had the optimistic outlook that these resources are forever expanding. However, the last decade had seen something of a stagnation in this regard, with a shift toward multi-CPU, multi-core, and distributed computing. In this regard, Julia's inception only 6 years ago gave it an edge compared to older languages, putting parallel and distributed computing at its center as one of its most important features.
One of the most serious barriers in the adoption of a new language is that it takes time for the ecosystem to catch up—and in the beginning, it cannot offer libraries of the quality and richness of the already established languages. This is less of an issue now, when Julia benefits from a large, enthusiastic and continuously growing developer community. But being able to seamlessly communicate with other languages is a very efficient way to enrich existing functionality and to effortlessly supplement any missing features.
Julia has the ability to directly call C and Fortran functions (that is, without glue code)—especially important for scientific computing, where these languages have a strong presence and a long history.
Optional packages extend this capability by adding support for calling functions written in other languages, most notably Python, via
PyCall. And there are others, supporting interaction with Java, C++, MATLAB, Rust, and more.
The REPL represents a language shell, an interactive computer programming environment at the command line. Julia has an excellent REPL, supporting sophisticated code inputting and evaluation. It includes powerful editing features such as searchable history, tab-completion, and syntax highlighting, to name just a few.
It also comes with three special modes—shell, which allows executing commands as if at the OS Terminal; help, which provides access to documentation without leaving the REPL; and pkg, used for installing and managing application dependencies.