5 Awesome Things To Love About The Julia Language | by Emmett Boudreau | Towards Data Science

I have been using (and praising) the Julia programming language for years now. As of today, I have written 346 articles here on Medium, and those articles started from one source article, which was about the Julia language. In that article, I touched on the things a Data Scientist might want to know before picking up the Julia programming language. If you would like to read that article, you may certainly look into it here:
towardsdatascience.com
During my experience in Julia, which I believe to be a lot after all the modules, projects, and research I have written in Julia, there have been a number of features that I have simply found to be incredibly convenient. Today I wanted to reveal some of the cool features of the Julia language, why I think that those features are awesome. In some instances, I might even provide some real world examples where this has come in handy, and then compare this to other languages in the Data Science and Software Engineering domains. This will help us to get a better idea of why exactly this is a great feature, how it can be used, and where the industry is in comparison to Julia.
In my subjective view of this language, I feel it definitely has a lot of potential to help move things forward in at least the Data Science domain. Let us detail some of the great features about Julia, and why they happen to be so great.
If we refer to the Julia Github repository, we can see a very interesting balance of languages that are used for the core of Julia.
The Julia programming language is written in 69-percent Julia, itself! This provides a host of advantages when compared to most other programming languages, as some are written in C or Assembly, and often the actual base of the language is barely written in the language’s actual syntax. This means a few different things of end-users of the language like myself.
Researching the actual source-code for a portion of Julia that might not be documented super well is incredibly easy.
If someone had a propose change for the language, but did not know the first thing about C, C++, or the LLVM compiler library, they could still make contributions to the language.
Getting even more crazy, one could even write their own edition of the language and use it instead of the core language.
Contributing to the first point, I have been working on a Julia package called Jockey.jl. Jockey.jl is a notebook server session for the Julia programming language that I am developing. My original intention with this package, however, was to put notebooks into the terminal. I just thought given how awesome the Julia REPL actually is, maybe there could be a notebook session on the top half of the REPL, and then the regular Julia REPL below it.
Of course, that kind of work is going to require that we have knowledge of the Julia REPL’s internals, in order to alter the behavior of that terminal. The problem, however, is that the language does not have any explicit documentation on the Base.Terminals module. You can view the documentation markdown file, which does carry pretty well into Github-style markdown here:
github.com
Most of this documentation is meant for end-users, which I suppose makes a lot of since, since generally I think people are going to be concerned with how to use the REPL, change key-binds, that sort of thing rather than using the package to change the function of the terminal. Fortunately, since I was able to look through the source code of Terminals.jl, LineEdit.jl, and REPL.jl, I was able to get a simple, very buggy version of this package with that feature released. While this instance of using this was very cool, I did end up giving up on that approach to notebooks because I found it to be a hassle to use, as the terminal and the terminal’s cursor in tandem with having a REPL at the bottom was simply a lot to manage.
It is cool that even though I do not know the internals of the language, and have not really worked on the language, I was still able to go into this package and read all of the source code. And this would be the case even if I knew no other language except for Julia.
Multiple dispatch is a very obvious thing to love about the Julia programming language. The core paradigm of Julia revolves around this polymorphic concept. That being said, while multiple dispatch is certainly available in other programming languages, I genuinely believe that the Julia programming language has perfected the multiple dispatch concept. I detail exactly why I think that is in this old article:
towardsdatascience.com
That being said, I think I have talked about multiple dispatch a lot, and although it is a great way to program in my opinion, I think that beating a dead horse on it is probably not optimal. However, many who have not used Julia, or are new to Julia do not realize how constructors can be used with multiple dispatch to create some pretty incredible results. Consider the following constructor:
struct NormalDist <: Distribution           σ::Float64           μ::Float64           N::Int64end
This is a regular constructor, the actual call for this constructor registered into Julia will look like this:
NormalDist(::Float64, ::Float64, ::Int64)
If we were to call the distribution with those values, we would get a return of a new NormalDist type. However, making someone calculate the standard deviation, the mean, and put the number of observations in themselves does not seem optimal, and is not typically how normal distribution types like this work. That being said, we can use multiple dispatch to write a new version of this call that will call a function instead by building an inner constructor:
struct NormalDist <: Distribution           σ::Float64           μ::Float64           N::Int64        function NormalDist(x::Array)               N = length(x)               σ = std(x)               μ = mean(x)               new(σ, μ, N, cdf, apply)        endend
Now we have two registered calls, one for the constructor and one for the inner constructor, which is a function that will return a new version of our constructor, which I will now refer to as the outer constructor. Now we have two calls, one is the regular constructor that we had before,
NormalDist(::Float64, ::Float64, ::Int64)
and the other is our new function:
NormalDist(::Array)
Whenever this function is used, it does not matter what the programmer on the other end intends to call. If they happen to have the standard deviation, mean, and N and provide those arguments, then we will get the type. If they do not and only pass the array, then they get the constructor. We can also build an infinite (besides in terms of hardware limitations,) number of these inner constructors in order to make our constructor have all the calls we could possibly want.
When it comes to Julia, the most obvious comparison to make is always going to be Python. Although in paradigm and syntax Julia is slightly different, the languages have a similar market niche and perhaps a similar objective. In the Python edition of this normal distribution, we instead define the type and then the equivalent to it is our __init__() function.
class NormalDist:     def __init__(self, x):               self.N = length(x)               self.σ = std(x)               self.μ = mean(x)
We lose performance with this function with all of the assertion we have to do to the things provided to the initialization. We also would not be able to make an infinite number of calls for different types an end-user might want to use.
We can also pass functions and types where we do not even know what type it is through an inner constructor. I have a video from the Comprehensive Julia Tutorials that goes into detail on constructors that you may watch by ctrl+clicking this text. That might provide a bit more information on constructors in Julia and all of the ins-and-outs, and if you are interested in learning Julia, that tutorial series might be a good place to start!
Now let us talk about the actual Julia compiler, and its speed. I must confess — there are a lot of ins and outs of bench-marking programming languages, and frankly fundamental problems with comparing benchmarks from said programming languages. That being said, I do not ever think that benchmarks are the end-all be-all of programming language speeds, as I am certain there are many instances where different languages might be faster at certain operations. That being said, however, we can still observe from benchmarks that Julia is obviously a pretty fast language.
I must confess, it is impossible to know how much of this JIT is responsible for. However, what I can say is that Julia is fast. This is especially the case when compared with languages that have similar syntax. The language typically falls around about the same speed as C and Rust. In some cases, those three can trade speeds in benchmarks between who is ultimately better. Some are amazed, and do not believe to learn that Julia can actually be faster than C. An important note to make, however is that those bench-marking measurements exclude precompilation of packages.
That being said, despite all of that the fact remains that Julia is fast. This is an advantages, especially when it comes to Data Science applications where some might be working with a lot of observations and operations on that data.
Another awesome thing in Julia is the package manager, Pkg. Compared to a lot of other package managers I have used in my experience as a programmer, Pkg is one of if not the best. At this rate, I wish I could use Pkg as the package manager for my entire operating system. The package manager handles all of the dependencies for us very easily, can generate project directories, and does so with ease. I think that handling virtual environments environments from within the package manager is far more ideal in my subjective opinion.
While I think other languages do have some pretty awesome package managers, I think Julia’s package manager is simply better. The package manager and TOML setup that the Julia language has tends to work very well. Packages and their respective dependencies are completely managed by Pkg, which makes convenience a key feature of this package manager and its capabilities.
Comparing this package manager to Node, as an example, I think that the way packages are added is simply a lot better. Typically in node, you would call npm in a project directory, which can be a bit confusing with some projects because sometimes this is not the home directory. Taking the same comparison over to Python, environments are a lot harder to source, and are nowhere near as easy to manage as Pkg packages. Being able to manage packages and environments with Pkg, and using Pkg as a regular Julia package means that there is a lot of flexibility with controlling Pkg to do different things.
The last Julia feature that I wanted to share is the Julia’s REPL. When it comes to REPL’s for programming languages, there are certainly some desirable options. Python’s REPL is a decent REPL, though it is somewhat minimalist. My favorite REPL prior to my experience was probably the Steel Bank Common Lisp (SBCL) REPL, as I found the declarative and functional nature of keeping the data in tact using saved states that can be reverted to be a really awesome feature. That being said, Julia completely toppled this REPL with its interactive, and dynamic REPL.
As I touched on in No1, I have worked with the REPL’s source code and types. The REPL is incredibly flexible, and has a lot of different capabilities. In a normal REPL-work scenario, cding around, listing directories, all of these things is sometimes not possible, or requires a method call in the programming language. Calling cd(“dir”) over and over again in tandem with some LS method can be extremely tedious for exploring files or looking at environments. Julia counters this by providing a Bash REPL, which can be accessed using ; from the Julia REPL.
julia > ;shell > ls
Even more, Pkg also has its own REPL, which can parse regular Pkg commands and arguments to add and source packages. Consider the following REPL session:
(@v1.6) pkg> activate Jockey(@v1.6) pkg> activate Jockey  Activating environment at `~/dev/Jockey/Project.toml`shell> cd Jockey/home/emmac/dev/Jockeyshell> lsbin       config      Project.toml  routes.jl  testbootstrap.jl  Manifest.toml  public    src       usershell> cd src/home/emmac/dev/Jockey/srcshell> lscore  Jockey.jl  ServerControllershell> nano Jockey.jlshell>
It is pretty awesome to be able to work with directories in a conventional way that I am used to while also working with packages and files at the same time. It feels very fluid, and it feels like this is a very quick way to work in a REPL. This is especially the case whenever we compare it to other dull REPLs that do not contain these sorts of features and alternate REPLs.
Julia is my favorite programming language, and an awesome choice for any developer at that. I would highly recommend picking up the language if you are into scientific computing, as it is particularly good for that task. There are so many great things about Julia, but these are just a mere five that I think are incredible. If you would like to learn more about Julia in general, you can visit the Julia language website here:
julialang.org
Thank you very much for reading, and I hope some of these awesome things about the Julia language are maybe compelling enough to inspire some new learning. I think Julia is such an awesome language, and that is why I love to talk about and share its features, as this is the type of appreciation this fantastic language deserves. Have a fantastic day!