Julia is a just-in-time compiled (very, very similar to interpreted to me) programming language that attempts to provide ease of programming with near C computational speed, especially for numerical tasks. It can infer types (or accept static types), deal with Unicode, support parallel code execution, be run in a shell like an interpreted language, and interface with compiled code from other languages, mainly C. It has garbage collection that can be triggered manually (gc()).

The file suffix for julia code is .jl.

  • Indexing starts at 1 and not 0. Hmmm.

  • Single and double quotes mean different things ('c’har vs. "string").

  • Good notes about julia.

Obtaining

The Julia download page is a GitHub thing. It has a "Download ZIP" button, but this is not recommended. It makes an inconsistent tree of Julia. A better way is…

git clone git://github.com/JuliaLang/julia

Then…

cd julia
make

Takes forever.

Running Code

Just executing the julia command will run an "interpreter" that compiles and executes each line as it is entered.

Code can be compiled and run directly:

julia -e 'help()'

I find this kind of ominous:

$ time julia-v0.1.2 -e 'quit()'
real    0m9.611s

Yikes. Looks like this is being worked on. Indeed, here is a hint of things moving in the right direction:

$ time julia-v0.3.0-prerelease+1406 -e 'quit()'
real    0m1.582s

Compare to Python 2.7.4:

$ time python -c 'raise SystemExit'
real    0m0.052s

If you’re going to compile it from source, start with installing gfortran, m4, libncurses5-dev, and possibly others. Then git clone git://github.com/JuliaLang/julia.git and make.

Style

Comments use #.

Variables

Variables…

  • …must start with [a-zA-Z_]. Underscore is discouraged.

  • …are case sensitive. Normally lower case (functions and macros too).

  • …do not imply type, etc.

  • …can contain unicode messes.

  • …can overwrite many important things (pi=3.1).

  • …can not overwrite reserved statements (if=4).

Oddly functions which modify their input have names that end in !. These are "in-place" functions. I guess like sort! vs. sort, where the former returns the list sorted and the latter returns a new copied sorted list with the original unsorted.

Using the const keyword makes that variable into a constant. This improves performance where possible.

const TOLERABLE_ERROR = 0.000001

Types

There is a typeof() function if you’re unsure. Also typemin() and typemax() are handy to find out the numerical range limits of various types.

Table 1. Some types with ranges

Int8

[-128,127]

Int16

[-32768,32767]

Int32

[-2147483648,2147483647]

Int64

[-9223372036854775808,9223372036854775807]

Int128

[-170141183460469231731687303715884105728,170141183460469231731687303715884105727]

Uint8

[0x00,0xff]

Uint16

[0x0000,0xffff]

Uint32

[0x00000000,0xffffffff]

Uint64

[0x0000000000000000,0xffffffffffffffff]

Uint128

[0x00000000000000000000000000000000,0xffffffffffffffffffffffffffffffff]

There is a BigInt type for arbitrary precision work (internally uses GNU’s GMP). Otherwise overflow will wrap around modolo like. Here’s an example of BigInt in action:

@printf("%.70f",1/BigFloat("9998"))
0.0001000200040008001467614281998486092106759315356612205505371093750000
Note
This is not exactly the fascinating answer the answer that http://wolframalpha.com gives. Hmm. Try it.

Numbers with decimal points are Float64 unless told otherwise with a definition like 0.5f0 in which case it’s a Float32. Hex works with something like 0x1p0 (internally a Float64).

For weird math fans, there is a positive and a negative 0 representation. They are ==. There is also positive and negative inf and an unsigned nan. Full weirdness tools!

Concerned about binary to float conversion weirdness? Check out machine epsilon.

There is a function one(t) and a function zero(t) where t is type. This ensures you get a one or a zero that is exactly the kind you want.

You can cast things to be a specific type with the :: operator. Something like:

julia> typeof(3::Int64)
Int64

When attached to a variable, the :: declarations constrain the variable to that type like a statically-typed language, hopefully with all the performance benefits.

There is a bitstype type which allows you to define your own type which is a collection of arbitrary bits.

There is a composite type feature that produces effects like C’s struct.

julia> type Cd
       name
       seconds::Int
       end
julia> cd1= Cd("Lennon",133)
Cd("Lennon",133)

Instead of type you can use the key word immutable which will give you a structure that can not change once it’s instantiated. Or something like that.

Eval

Julia supports an eval command since program code is stored in the same data structures the code uses. This allows for "templates". My favorite application of this in Bash is to print out some code that will be run to see if it’s acceptable, and if so, run it.

Operators

To me, the defining feature of Julia’s syntax is the ability to omit * when writing many multiplication idioms. For example, 3x, (x-1)x, 2(x+3)^2, etc. However, (x+3)(x-1), apparently is not cool. Avoid things that look like functions, e.g. x(y+3). Use cautiously.

All the normal operators are present. "Not" is ! for bool and ~ for bits. I found $ interesting for xor. Updating operators like += work fine.

Some special weird math functions are isfinite(), isinf(), and isnan(). Also isequal() is the more exacting friend of ==.

Boolean types can be considered 1 (true) and 0 (false) in operations like x > true. This allows the bool producing operators to be chained.

Math Functions

  • round() iround()

  • floor() ifloor()

  • ceil() iceil

  • trunc() itrunc()

  • div(x,y) - Truncated division, like Python 5/2.

  • fld(x,y) - huh?

  • rem(x,y) - remainder, different from mod in sign matching.

  • mod(x,y) - Python’s %

  • gcd(x,y,z,…) - Greatest common denominator.

  • lcm(x,y,z,…) - Least common multiple.

  • abs()

  • abs2() - Absolute value squared.

  • sign() - Returns -1, 0, or +1

  • signbit() - Similar to sign but technical.

  • copysign(x,y) - Magnitude x, with sign of y.

  • flipsign(x,y) - Magnitude x, with sign of x*y.

  • sqrt(x)

  • cbrt(x) - Cube root.

  • hypot(x,y) - Hypotenuse lenght of right triangle with x and y length sides.

  • exp(x) - e to the power.

  • expm1(x) - accurate exp(x)-1 function for x near zero.

  • log()

  • log2()

  • log10()

  • exponent(x) - binary exponent of x, whatever that means.

  • significand(x) - related to exponent().

All the trig functions seem present from sin() to acsch() and they seem to operate in radians. There are a set of trig functions that work in degrees too like sind().

All kinds of special functions are included out of the box like besselj0(x) and zeta(x) and erf(x) and on and on.

Complex Numbers

Things like (1 + 2im)*(2 - 3im) are possible.

Rational Numbers

This interesting - fractions can be defined as fractions with //. So 1//7 is one seventh. This means that isequal(1//3,.3333333333) is false. I don’t know what esoteric good this does since isequal(1//3,1/3) is true. But I’m not a mathematician.

Arrays

Arrays are quite well developed. They tend to be suited to technical tasks and multi-dimensional tricks. Extensive documentation exists

To initialize an Array:

bins= fill!(Array(Int,bincount),binsize)

Dicts

These are officially called associative collections.

They are created and used like this:

julia> pitch=Dict() # Probably better in this case:Dict{ASCIIString,Int32}()
Dict{Any,Any}()
julia> pitch["A"]=440
440
julia> pitch["C"]=523.3
523.3
julia> pitch
{"A"=>440,"C"=>523.3}
julia> states=["CA"=>"California","AK"=>"Alaska","OH"=>"Ohio"]
["CA"=>"California","OH"=>"Ohio","AK"=>"Alaska"]
julia> has(pitch,"C")
true
julia> delete!(pitch,"C")
523.3

Functions that work on Dicts include:

has (not haskey as it says in the manual) get (same as d[key] syntax. getkey - returns key or the default supplied. delete! - Delete an item from a list. pop! - Delete and return item or return default. keys - Returns an iterator of keys. collect(keys(d)) - Creates an array of keys. values - Returns an iterator of values. collect(values(d)) - Creates an array of values. merge - Creates a new dict from the supplied dicts. merge! - Updates first dict with the other ones. sizehint(s,n) - Reserve capacity of n elements for collection s. An optimization.

You can iterate over Dicts like this:

for dictitem in adictionary
    println(dictitem[1]," ",dictitem[2]) # Produces "key value".
end

Strings

Strings are not exactly a real "type" but rather an "abstraction". This means that many types can have a "string" representation. I guess it’s like Python’s __str__().

There are "Char" types for single characters which can be specified with single quotes.

Unicode is painstakingly supported. But you you’re reading this, you can probably just pretend to always use English and not worry about the details.

Julia has Perl-compatible regular expressions (PCRE). Simple usage is something like ismatch(r"^.o.k...","Monkeys") producing true. There is a match() function which returns the match. There is all the rest of the normal and fancy functionality. The whole story.

Strings support all kinds of slice tricks. A cool one is mystring[end] and things like mystring[end-3] or mystring[end/4]. There is the normal Python-like slice, but something like abc[2:5] (where abc is the alphabet) returns bcde.

Commas concatenate strings into a list. Asterisks concatenate strings into a bigger string.

It’s possible to use $ to cause parts of strings to be interpreted like Perl or Bash such as, "This is the alphabet: $abc". This looks like it can make a mess. Of course to include a real dollar sign you need to now do something special like "\$". See the macro example (monetize) above.

Not to be outdone by Python’s str*n.

julia> ^("-=",10)
"-=-=-=-=-=-=-=-=-=-="

Handy string functions:

  • endof()

  • length

  • start()

  • c,j= next(str,i)

  • ind2chr(str,i)

  • chr2ind(str,i)

  • split(str,[sep,[limit,][empty?]]) - Separators can be char, string, or re.

Functions

Functions can be defined like this:

function f(x,y)
    x + y
end

Or:

f(x,y) = x + y

This is called with:

f(3,4)

Functions are objects and can be moved around with:

g = f
g(3,4)

Function arguments are passed "by sharing" though I fail to understand how that’s different from passing by reference. Whatever you call it, your objects can get modified when fed to a function.

There is a return keyword which can proffer some specific result the function should return, but it also suffices to just use the function’s last statement’s result which will be the default returned item.

Normal operators are functions and this can be delightful if you need to write some obfuscating code.

julia> *(+(3,7),+(5,5))
100

Or

julia> function exciting!(x); x*x; end
# method added to generic function exciting!
julia> exciting!(10)
100

Anonymous functions are supported like Python’s lambda.

julia> r -> r^2+2r+3
# function

This can be used to feed the map function.

Python Function Similarities

Tuples can be returned if multiple values need to be returned. Parentheses are optional. This is all just like Python.

There is a way to specify a variable number of arguments. Basically somefunction(a,b,x...) will need an argument for a and one for b and an iterable object for x (a tuple, list, etc.).

Optional arguments can be preset like rotate(angle,radian=true). In this case, radian does not need to be specified unless needed.

There are keyword based arguments. This allows functions which could have many possible arguments or options but only a few essential ones. function cake(diameter, flavor; icingflavor="chocolate", sprinkles=false, text="Happy Birthday!")

There is even an interesting structure that reminds me of Python’s list comprehension.

julia> map( [1,2,3] ) do x ; x+3 ; end
3-element Int64 Array:
 4
 5
 6

Macros

Macros are a bit like replacing it’s "call" with some code to be eval’ed.

julia> macro monetize(x)
       "\$" * string(x)
       end
julia> print(@monetize(33.25))
$33.25

Note that it’s easy to make a mess of things with macros (scope, clashing, etc). You have been warned.

Flow Control

Julia allows for sequence of expressions to be evaluated and the last one returned as part of another expression.

julia> h=(x=3;y=4;sqrt(x^2+y^2))
5.0

if - And Other Conditional Branching Tricks

The normal if, elseif, else syntax is normal and ends with end:

if x > 5
  println("Good job!")
elseif x > 0
  println("Better than nothing.")
else
  println("What's wrong with you?")
end

The if statement’s conditional must be true or false (not 1 or 0 etc).

The C style condition ? do_if_true : do_if_false syntax works.

The shell style || and && also work and only need to evaluate the portions necessary to do the right thing.

x=5; x>3 && x<10 || "non"=="sense"
true

while

julia> i=3; while i>0 ; print(i) ; i-=1 ; end
321

for

julia> for i = 1:3 ; print(i) ; end
123

Here the 1:3 is a "Range object". Mercifully this Python idiom is supported:

julia> l= ["ax","bow","club"]; for w = l ; println(w) ; end
ax
bow
club

There is a break and continue keyword. They do what Python programmers expect.

IO and Files

Input can be done from basic IO streams with something like:

read(STDIN,x)
readbytes(STDIN,n)
readline(STDIN)

Functions like show, print, and write can have an optional first argument that is one of STDOUT or STDERR.

write(STDERR,"Problem!")

Read each line of a file with something like this.

open("input_file","r") do f
    for line in EachLine(f)
        println("read line: ", chomp(line))
    end
end

Reading STDIN (a simple cat from standard input):

for line in EachLine(STDIN)
    println("read line: ", chomp(line))
end

External Programs

Backticks produce command objects:

julia> run(`grep xed /etc/passwd`)
xed:x:1000:1000:Chris,,,:/home/xed:/bin/bash
julia> me=chomp(readall(`whoami`))
"xed"
julia> run(`grep processor /proc/cpuinfo` | `wc -l`)
2

The $ can be used to interpret variables in these objects.

Error Handling

Julia can throw an exception. It can produce an error oriented exception. It can try code and catch exceptions. Regardless of how the tried code works, a finally clause can always run.

Interestingly, the manual says, "Don’t overuse try-catch. It is better to avoid errors than to rely on catching them." Sounds like something I’d say.

Consume and Produce

This arrangement assigns an on going task to a function and every time it’s called it makes progress in getting through the task. For example, if you need to go through a long input of numbers and for every number you need to do something to it, you could make a producer that gets each new number from the source. Then your consumer would request the next number and do something with it. This allows distinct functions to do the separate conceptual work, but interleave the execution.

TCP/IP

Interesting native client server functions. Documentation here.

Parallel Computing

Parallel computing features are highly developed. Full explanation here. Check out this cool ClusterManager functionality.

Calling C Code

Yes, it can be done. Details here.

Example

#!/usr/bin/julia
# Benchmark with a simple bin packing.o

const bincount= 5000::Int
const binsize= 100::Int
const maxitem= 70::Int

function next_item()
    r::Int
    r= floor(rand()*1000::Int) % maxitem + 1;
    return r
end

bins= fill!(Array(Int,bincount),binsize)
still_room= true

while still_room
    item= next_item()
    for i = 1:bincount
        if bins[i] >= item
            bins[i] -= item
            break
        end
        if i == bincount-1
            still_room= false
        end
    end
end

println( sum(bins) )

Julia Official Manual TOC