Julia is an interesting programming language.

It’s fast, expressive, when keeping a dynamic programming language.

From the perspective of Programming Language, I feel like to give some short introductions of Julia, in this structure:

Type system(think why I say “type system” for a dynamic language?)

Julia is likely to be the most expressive dynamic programming language.

It has LISP macros, with which can you manipulate your program in AST level.

Its low level IR(between AST and LLVM) is available to users, hence you could re-compile your functions, and change its behaviors.

It has a type system, which at least get rid of some unsoundness examples shown in Java. Specifically, it implements the “right” covariance, which works well with parametric types without suffering from the potentially incorrect coercions.

Let’s see the macro part first.

Julia ASTs are symbolic expressions(s-expressions), and available by using the macro syntax.

macro identity_process ( code ) code end macro get_ast ( code ) QuoteNode ( code ) end x = @identity_process 1 + 1 println ( x ) # 2 y = @get_ast 1 + 1 println ( y ) # 1 + 1 dump ( y ) # Expr # head: Symbol call # args: Array{Any}((3,)) # 1: Symbol + # 2: Int64 1 # 3: Int64 1

Syntactically constructing an AST node is allowed, by using quotation s.

expr1 = quote x = 1 y = 1 end expr2 = : ( x + y )

Further, the scope issues of macro system are solved by the support of hygiene and unhygiene. In a macro function, i.e, a function defined by leading keyword macro , if you return a esc(code) , the returned AST is unhygienic.

Q: What is the hygiene of macro?

A: The symbols used in the processed AST will not interact with the scope of macro callsites. Hence, the scope of macro callsites will not change, the binding, reading or writing of symbols are separate from the callsite.

Q: What is the unhygiene of macro?

A: You can refer the symbols from macro callsites. Hence, you can use macros to add new symbols, change value of a symbol, and so on.

This design permits the full featured use of LISP macros.

Next, let us focus on the type system.

Julia is strongly typed, hence you cannot coerce the type of a datum unlimitedly.

The type hierarchy is relatively clean.

You can upcast the type to its super type, and runtime type information will be stored as well. When downcasting the type to a specific one, type assertions will be performed on the stored runtime type information, to check its validity.

This mechanism contributes to Julia’s type safety.

Covariance in Julia looks pretty good

function F ( arg :: Vector { a }) where a <: Number a end F ([ 1 , 2 , 3 ]) # => Int F ([ "2" , "3" ]) # => string

Because in Julia, covariance successfully gets distinguished from heterogenous abstract types.

function G ( arg :: Vector { Number }) # or function F(arg :: Vector{a where a <: Number}) end G ([ 1 , 2 ]) # error!

Above codes report an error:

ERROR : MethodError : no method matching F (:: Array { Int64 , 1 }) Closest candidates are : F (:: Array { Number , 1 }) at ...

Why, isn’t a vector of integers a vector of numbers?

May not, because there’re ambiguities. When we say “a vector of numbers”, are we meaning “a vector of any numbers”, or “a vector of some kind of numbers”?

In Julia, things get distinguished.

A vector of some kind of numbers is Vector{a} where a <: Number , or Vector{<:Number} , which is the correct “generic” type.

A vector of any numbers is, Vector{a where a <: Number} , or Vector{Number} , which is heterogenous.

Besides, “some kind of numbers” belongs to “any numbers”, so Vector{Number} is an instance of Vector{<:Number} , and the converse doesn’t hold.

Additionally, Julia provides us with a type system including runtime type representations and useful operations, which are quite handy.

sort ( # T[a, b, c] is a vector of type Vector{T} Type [ Vector { a } where a <: Integer , Vector { Int }, Vector { a } where a <: Number ], lt = ( <: ) )

We got

3 - element Array { Type , 1 }: Array { Int64 , 1 } Array { a , 1 } where a < : Integer Array { a , 1 } where a < : Number

Vector{a} is an alias for Array{a, 1} .

As for a higher level interface for user programming, Julia has parametric functions. It’s called multiple dispatch, which provides ad hoc polymorphisms, including virtual dispatch(runtime) and static dispatch(compile time).