So if you are one of those cool kids meta programming in Ruby or you love all the tools, libraries and clarity of Python, why would you consider Julia. The Julia website does go into a lot of reasons why you should consider having a look at Julia, so I am not going to reiterate those here but instead give my own personal take on what I like.

A lot of this comes down to what you think about type systems. While I tend to favor dynamic languages I am not a purist. I did notice quickly how nice it was to work in Google Go comming from Python. Now I really dislike typing in languages such as C++ and Java, because I feel it always gets in my way and so much code is spent juggling around the type system.

On the other hand the downside I see with Python and Ruby is not just that they are fairly slow, but I actually quite like to specify type now and then in my programs. That is not because I think that static typing is crucial to catch all kinds of important bugs. But I believe using types helps readability.

Here is an example from C++ of copying the values of a dictionary to an array:

std::transform( m_str_T.begin(), m_str_T.end(), std::back_inserter( v_T ), boost::bind( &std::map< std::string, T >::value_type::second, _1 ) );

It is hard to see at a glance what is going on because there is so much noise comming from spelling out to the compiler everything it needs to know about the types of everything. If you do this in Julia (similar for Python) it would look like this:

array = [value for (key, value) in dict]

You can quickly see what is going on because the code is not riddled with type information. However we should not ignore static typing completely. Consider this Ruby code from previous blog entry:

class Facility def initialize @occupier = nil @duration = 0 end end

If I am to reason about this code I have a number of issues. I have no idea what sort of object goes into @occupier . I will have to locate some function which puts somethig into @occupier and see what the type of the object is there. Further I can’t know for sure whether duration is a continous or discrete value. At work I mostly look at Objective-C code which usually has some static type information. However collections are not annoted with what objects are stored in them. When I analyze some new code I usually look at the class definitions of the main objects used in the application to get an overview of what is going on. A common stumbling block is that Objetive-C collections don’t tell what type of objects they contain.

That is something I like about Julia. If you look at the definition below:

type Facility occupier::Union(Person, Nothing) duration::Int32 Facility() = new(nothing, 0) end

You can see that it tells you a lot more than the Ruby example. Duration is a discrete number and the occupier of the Facility are objects of type Person.

Multiple Dispatch

Julia is not object oriented. And I think that is a good thing. For the things I usually work with I find it constraining and unatural to model the world as if methods must always belong to one particular object. Polymorphis lets you chose what concrete method to call at runtime depending on the type of the instance you invoke the method on. But limiting yourself to deciding this based on one object is an artificial constraint. E.g. when programming a C++ game engine you might want to calculate whether two geometric objects in your scene are overlapping. You don’t know what type they are at runtime so you can’t use C++ templates. So what you end up with is some convoluted way of doing it where each of your geometric objects need to implement a collide method which gets dispatched at runtime. While you have another method overloaded for each concrete geometry type:

bool CircleShape::collide(Shape* other) { if (!boundingBox().intersect(other->boundingBox())) return false; return other->intersection(this->circle); }

Don’t worry you don’t really have to get the details of this. With Julia I could simply write a bunch of:

function collide(me::Circle, other::Rectangle) function collide(me::Polygon, other::Circle) function collide(me::Polygon, other::Rectangle)

And at runtime when I call:

collide(me, other)

It would dispatch to the correct implementation, because in Julia the vtable is essentially stored on each function rather than on each class. When you call a function in Julia it will lookup in a table at runtime which concrete function it should call based on the types of its arguments. I find that many design patterns such as Visitor just exist because of this flaw in how Object Oriented Programming works. While my example was C++, the problem does not go away just because you chose Ruby or Python instead.

Properly modeling your domain

In geometry a 2D point and a vector are not the same even if they both have an x and y component. You can perform many of the same operations on a point and vector but they do not always work the same. So you need to be able to distinguish them. In Julia we can model them as two distinct types:

type Point x::Float y::Float end type Vector2D x::Float y::Float end

In geometry if I add a vector to a point I get a new point. But if I add a vector to a vector I get a new vector. I can model this in Julia like this:

+(p::Point, v::Vector2D) = Point (p.x + v.x, p.y + v.y) +(u::Vector2D, v::Vector2D) = Vector2D(u.x + v.x, u.y + v.y)

This would not be possible in Ruby because it would not be able to distinguish between a Point and Vector2D argument. You would have to give each function a different name and keep track of when and where you are giving a point or vector as argument. One might ask if it matters. When working in C++ i often model points, vectors and unit vectors with exactly the same class. However e.g. having your own type for unit vectors gives you some neat possibilities. In code it is usually troublesome to keep apart which of your Vector2D objects represent reqular vectors and which are units vectors. Say we define a new type to represent unit vectors:

type Direction x::Float y::Float Direction(v::Vector2D) = (len = norm(v); new(v.x/len, v.y/len)) end

Unit vector are special because among other things their length is always 1. So while Vector2D would define the following functions as:

norm(v::Vector2D) = sqrt(v.x^2 + v.y^2) sqrnorm(v::Vector2D) = v.x^2 + v.y^2 unit(v::Vector2D) = Direction(v)

Notice that there is only one constructor for a unit vector, which takes a Vector2D . There is thus no way to make a unit vector which does not have norm 1 (eucledian distance of 1). This allows us to define the previous functions for a unit vector as:

norm(v::Direction) = 1 sqrnorm(v::Direction) = 1 unit(v::Direction) = v

Thus a unit vector can calculate norm really fast. Thus in code where it does not matter whether you are using a unit vector or a regular vector you can pass in any and perform calculations in the optimal way.

Type Unions

Although you might think all this seems very neat, you might already be thinking that defining 3 different types is going to cause a lot of code duplication because many of the functions you can perform on points, vectors and unit vector are the same. However Julia allows you to use type unions. E.g. the dot product works the same way for unit vectors and vectors. But you don’t need to define it twice. In the implementation you can say you don’t care whether the input is of type Vector2D or Direction :

VecOrDir = Union(Vector2D, Direction) dot(u::VecOrDir, v::VecOrDir) = u.x*v.x + u.y*v.y

Tuning and Performance

So I covered a lot about why I think Julia is great at modeling your problem. Basically how powefull Julia’s abstraction mechanisms are. The other reason I like Julia is due to the promise of great performance and the ability to tune and reason about the performance of your code. There is a function code_native which lets you look at the assembly code the JIT compiler would generate for a function. With it you have a powerfull tool to inspect potential performance issues with the way you write your code. One thing I was curious about was e.g. whether using union types would be bad for performance because I would essentially box and unbox data into a sort of Variant data type. But checking the code that would be called when both arguments are of type Vector2D we can see that there is no boxing or conversion going on. Just raw calculations:

julia> code_native(dot, (Vector2D, Vector2D)) .section __TEXT,__text,regular,pure_instructions push RBP ; Store stack frame pointer mov RBP, RSP ; Let framepointer point to stack ; Move data at address RDI + 8 to ; floating point registers XMM0 and XMM1 vmovsd XMM0, QWORD PTR [RDI + 8] vmovsd XMM1, QWORD PTR [RDI + 16] ; Multiply double number in XMM1 floating point ; register with number at RSI + 16 store result at XMM1 vmulsd XMM1, XMM1, QWORD PTR [RSI + 16] vmulsd XMM0, XMM0, QWORD PTR [RSI + 8] ; Add results from both multiplication and store at XMM0 vaddsd XMM0, XMM0, XMM1 pop RBP ret

If you are familiar with assembly code it sould be clear that Julia has the potential to go almost as fast as C code despite being primarly a dynamically typed language. This sort of optimized code would be much harder to generate for Python or Ruby because the JIT compiler can’t know the type of arguments being passed.