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?
Here is an excerpt from the Getting Started with Julia Programming Language book:
This is the example function in the book:
I'm not sure if the book is right, but it contradicts all the other answers.
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 thatx
is a value of typeT
; if that is true,x::T
evaluates to the value ofx
, 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 ofx
is of typeT
.[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.
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)
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.