Julia might be the newest language covered so far, released just in 2015, and meant to replace older (and generally awful) languages for scientific computations. Let's see how well it does.
As an interesting side note, Jupyter Notebook is named after Julia, Python, and R, even though in practice it's used for Python, Python, and Python. I guess PyPyPy Notebook just doesn't sound right.
Nothing surprising here:
function fib(n) if n <= 2 1 else fib(n-1) + fib(n-2) end end for i=1:30 println(fib(i)) end
The syntax is very close to Ruby and Python. Functions don't need explicit
function fizzbuzz(n) if n % 15 == 0 "FizzBuzz" elseif n % 5 == 0 "Buzz" elseif n % 3 == 0 "Fizz" else n end end for i=1:100 println(fizzbuzz(i)) end
Everything works just fine, and is perfectly clear. Time to do something a bit harder.
Julia has builtin support for vectors and matrices, so we can do very fast Fibonacci formula with them, similar to what we did with Octave.
u = [1 1] m = [1 1; 1 0] for i=1:40 result = (u * (m ^ (i-1))) println("fib($i) = $result") end
Which generates what we expect:
$ julia fibmatrix.jl fib(1) = 1 fib(2) = 1 fib(3) = 2 fib(4) = 3 fib(5) = 5 fib(6) = 8 fib(7) = 13 fib(8) = 21 fib(9) = 34 fib(10) = 55 fib(11) = 89 fib(12) = 144 fib(13) = 233 fib(14) = 377 fib(15) = 610 fib(16) = 987 fib(17) = 1597 fib(18) = 2584 fib(19) = 4181 fib(20) = 6765 fib(21) = 10946 fib(22) = 17711 fib(23) = 28657 fib(24) = 46368 fib(25) = 75025 fib(26) = 121393 fib(27) = 196418 fib(28) = 317811 fib(29) = 514229 fib(30) = 832040 fib(31) = 1346269 fib(32) = 2178309 fib(33) = 3524578 fib(34) = 5702887 fib(35) = 9227465 fib(36) = 14930352 fib(37) = 24157817 fib(38) = 39088169 fib(39) = 63245986 fib(40) = 102334155
Vectors and matrices use 1-based indexing, which is totally cringe, even if that's what pre-computer-era mathematicians are used to.
Julia supports string interpolation with
"$x" for variables and
"$(...)" for expressions. It's somewhat disappointing that we finally arrived in the world where every sensible language supports string interpolation, but every single one of them chose different syntax for it.
strings = ["Hello", "Żółw", "🍩"] println([length(s) for s in strings]) println([uppercase(s) for s in strings])
Given that it's a new language, it handles basic Unicode operations correctly as expected:
$ julia unicode.jl [5, 4, 1] ["HELLO", "ŻÓŁW", "🍩"]
Also notice Python-style list comprehensions.
Take a guess which letter it would print:
s = "Żółw" println(s)
Did you guess correctly?
$ julia unicode2.jl ó
In every sensible language that would be
I already mentioned that Julia uses 1-based indexing, even for strings, so you might have guessed
ł instead, but how the hell did we get
It turns out that Julia indexes characters by their byte position, starting at 1. So for string
"Żółw", these are the letters:
And every other index raises an exception like:
julia> "Żółw" ERROR: StringIndexError: invalid index , valid nearby indices =>'Ż', =>'ó'
We can call
collect(eachindex("Żółw")) to get a list of the valid indices
[1, 3, 5, 7].
println([1 2] == [1 2]) println([1 2; 3 4] == [1 2; 3 4]) println((1,2) == (1,2)) println((10:20) == (10:20))
It's not much surprise that
== works correctly on all complex types like vectors, matrices, tuples, ranges, and so on:
$ julia equality.jl true true true true
Julia supports many different styles of functional programming:
numbers = (1:10) println( [x for x in numbers if x % 2 == 0] ) println( filter(iseven, numbers) ) println( filter(x->x%2 == 0, numbers) ) println( filter(numbers) do x x%2 == 0 end )
Notice that block argument goes last with
do syntax, but it's translated to be first argument of the function.
Like other mathematical programs, operators can be applied to collections element-wise. Julia also provides convenient syntax to use it with your own functions:
double(x) = 2*x println(double(21)) println(double.(1:10)) println(10 .+ [1,2,3])
$ julia dot.jl 42 [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] [11, 12, 13]
.() are like implicit
map for the appropriate types. This isn't something most general-purpose languages typically do, but it's much more common in mathematical code, so many mathematical languages have some degree of support for this.
I mentioned back in Emojicode episode that languages really torture their ASCII symbols to avoid using anything outside ASCII. Operators often consist of 2-3 symbols, and the same combination is often overloaded to mean a lot of different things. Julia takes modest steps to use Unicode mathematical symbols.
# is subset println([1,2] ⊆ [2,3,1]) # is element println(3 ∈ [2,3,1]) # some trigonometry and square roots println(sin(π/3) * √3)
true true 1.4999999999999998
Julia doesn't go overboard, I think in the future this use will become more common.
Unusually for a non-Lisp, Julia has macros. They don't look quite the same as regular Julia syntax, but it's a thing:
macro unless(condition, expression) quote if !($condition) $expression end end |> esc end println("Enter number:") num = parse(Int, readline()) if isodd(num) println("$num is odd") end @unless isodd(num) begin println("$num is even") end
Which works just as expected:
$ julia macro.jl Enter number: 69 69 is odd $ julia macro.jl Enter number: 420 420 is even
I don't know how much that matters in practice. A number of non-Lisp languages tried that, and none of those attempts really did much. Julia in general is very generous with the syntax.
$ is a "quasi-quote" in Lisp terms.
|> is just Elixir style piping into another function and not specific to macros. You can do
-42 |> abs if for some reason you don't want to type
As you can see as Julia isn't a Lisp, syntax introduced by macros doesn't look quite the same - there's extra
begin there. But maybe it would be good enough for some use cases.
For the kinds of tasks Julia is aiming at, I'd generally recommend trying out Python first, but Julia isn't a bad choice.
It's a fairly modern language with limited baggage, friendly syntax similar to Python and Ruby, and you can use the familiar Jupyter Notebook style of development. Many new languages like Go, Rust, and TypeScript were obviously created by mental boomers enamored with distant past, and who rejected many of the lessons of modern programming language design. Julia doesn't feel like it - it feels like they got most of the things right, given what they were aiming for.
Most other "mathematical" languages have terrible quirks, massive baggage, awful syntax, are seriously underpowered once you step outside their core domain, and are often proprietary or semi-proprietary. By comparison, Julia does decently on all these criteria. If together with Python they ends up euthanizing some crappy closed source languages still used in the science world, that would make the world a better place.
I didn't investigate Julia's performance at all. Honestly I think the whole "performance" stuff is really overrated, and microbenchmarks are completely without any real world significance, but if you're into such things, Julia's performance claims are here.
Overall, I think it's highly promising, even if I'd probably still try Python first.