A lot of blogs, and the manual itself, say that Julia is dynamically typed.
But from my reading of the manual, it sounds to me more like it is
statically typed with type inference, like F#.
- Is Julia statically typed with type inference?
- Is it dynamically typed?
- I'm assuming it is dynamically typed, it seems unlikely the manual is wrong.
- Is type inference involved in Julia at all?
Tim Holy's answer is quite correct, but I'll elaborate a bit. First, let's define some terms – you may disagree with my definitions, but at least you'll know what I'm saying. The primary difference between static and dynamic languages, in my view, is this: in static languages, expressions have types; in dynamic languages, values have types.
In a static language, there are rules for determining the type of every expression in a program. The types of expressions dictate the behavior of the program. A program that doesn't admit a consistent type to be determined for every expression is considered incorrect and will not compile. In the presence of polymorphism, the type of an expression may not be a single concrete type: parametric polymorphism can be thought of as a way of letting the same code describe a whole family of concretely typed algorithms, indexed by the parameters of the types; subtype polymorphism can be thought of as introducing a limited amount of dynamic behavior into an otherwise static language.
Dynamic languages, on the other hand do not have rules for assigning types to expressions: types are implied by the way data flows through the program as it executes. In general, expressions can potentially produce values of any type at all. Because of this, type theorists sometimes describe dynamic languages as "unityped" – i.e. from the static perspective, where a "type" is inherently a property of an expression, all expressions in a dynamic language have the type Any
. Of course, that's applying the static notion of type – which is only meaningful for expressions – to a language where the notion of type is only meaningful for values.
Julia is squarely in the dynamic camp: types are a property of values not expressions. The result type of code is determined by how values flow through it when it executes; the language does not include any rules for assigning types to expressions before executing them. Unlike many dynamic languages, however, Julia has a fairly sophisticated language for talking about types, and you can annotate expressions with types. For example, x::T
is an assertion that x
is a value of type T
; if that is true, x::T
evaluates to the value of x
, otherwise an error is raised and the expression returns no value. Type annotations in method signatures have a slightly different meaning: instead of asserting the type of an existing value, they indicate that the method only applies if the corresponding argument is of the indicated type. In either case, the following code can safely assume that the value of x
is of type T
.
[Aside: In some languages with "gradual" or "optional" typing, type annotations switch the language from dynamic to static mode: methods without type annotations are dynamic; methods with type annotations are static. In static code, there are rules for assigning types to all expressions and the code must satisfy those. This is not the way Julia works – code with type annotations is still dynamic and has the same semantics as code without type annotations.]
Type inference in languages like F#, OCaml or Haskell is part of how the types of expressions are determined. If the compiler cannot infer the type of any expression, your program is broken and will not compile. These languages all use some form of Hindley-Milner type inference, which is a very clever way to derive the types of expressions from the structure of the code without having to write out explicit types (compare this to dynamic languages where the types are implied by execution of the code). Much of the time no type annotations are required at all, which is quite pleasant compared to the verbose type declarations which can be necessary in languages like C++, C# and Java. This is very different, however, from dynamic languages like Julia and Python where no type annotations are required simply because it is perfectly acceptable for expressions not to have a predetermined type. In Hindley-Milner languages, you may not have to write as many types as in C++ or Java, but every expression has a predetermined type that the compiler can compute.
Julia's compiler does type inference, but it is very different: it is not necessary for every expression to have an inferrable type. The compiler analyzes code to try to predict the types of expressions and uses that information to generate more efficient machine code. But if it can't determine the type of an expression, it's no big deal: the compiler just emits generic code that will work anyway, using run-time type information. For the most part in Julia, type inference is just an optimization – your code will work the same way with or without it – but with successful type inference, it will run a lot faster.
Both are true. Julia is dynamically typed, but in well-written julia code the types can usually be inferred. You often get a major performance enhancement when that is possible.
There's some discussion of this in the FAQ.
Here is an excerpt from the Getting Started with Julia Programming Language book:
It can also be useful to indicate the argument types to restrict the
kind of parameters passed when calling. Our function header for
floating point numbers would then look as function mult(x::Float64,
y::Float64). When we call this function with mult(5, 6), we receive an
error, ERROR: 'mult' has no method matching mult(::Int64, ::Int64 ),
proving that Julia is indeed a strongly typed language. It does not
accept integer parameters for the floating point arguments.
This is the example function in the book:
function mult(x::Float64, y::Float64)
x * y
end
mult(5, 6) # raises an error
mult(5.0, 6.0) # => 30.0
I'm not sure if the book is right, but it contradicts all the other answers.
It's dynamically typed, though if you specify a type like variable::type you can think of that variable as being statically typed (and this will improve performance in cases where the compiler could not automatically infer the type)