Types

Julia's type system is unique. Julia behaves as a dynamically typed language (such as Python, for instance) most of the time. This means that a variable bound to an integer at one point might later be bound to a string. For example, consider the following:

julia> x = 10 
10 
julia> x = "hello" 
"hello" 

However, one can, optionally, add type information to a variable. This causes the variable to only accept values that match that specific type. This is done through a type of annotation. For instance, declaring x::String implies that only strings can be bound to x; in general, it looks like var::TypeName. These are used the most often to qualify the arguments a function can take. The extra type information is useful for documenting the code, and often allows the JIT compiler to generate better-optimized native code. It also allows the development environments to give more support, and code tools such as a linter that can check your code for possible wrong type use.

Here is an example: a function with the calc_position name defined as the function calc_position(time::Float64) indicates that this function takes one argument named time of type Float64.

Julia uses the same syntax for type assertions, which are used to check whether a variable or an expression has a specific type. Writing (expr)::TypeName raises an error if expr is not of the required type. For instance, consider the following:

julia> (2+3)::String 
ERROR: TypeError: in typeassert, expected String, got Int64

Notice that the type comes after the variable name, unlike in most other languages. In general, the type of a variable can change in Julia, but this is detrimental to performance. For utmost performance, you need to write type-stable code. Code is type-stable if the type of every variable does not vary over time. Carefully thinking in terms of the types of variables is useful in avoiding performance bottlenecks. Adding type annotations to variables that are updated in the inner loop of a critical region of code can lead to drastic improvements in the performance by helping the JIT compiler remove some type checking. To see an excellent example where this is important, read the article available at http://www.johnmyleswhite.com/notebook/2013/12/06/writing-type-stable-code-in-julia/.

A lot of types exist; in fact, a whole type hierarchy is built-in in Julia. If you don't specify the type of a function argument, it has the Any type, which is effectively the root or parent of all types. Every object is at least of the universal type Any. At the other end of the spectrum, there is type Nothing, which has no values. No object can have this type, but it is a subtype of every other type. While running the code, Julia will infer the type of the parameters passed in a function, and with this information, it will generate optimal machine code.

You can define your own custom types as well, for instance, a Person type. We'll come back to this in Chapter 6, More on Types, Methods, and Modules. By convention, the names of types begin with a capital letter, and if necessary, the word separation is shown with CamelCase, such as BigFloat or AbstractArray.

If x is a variable, then typeof(x) gives its type, and isa(x, T) tests whether x is of type T. For example, isa("ABC", String) returns true, and isa(1, Bool) returns false.

Everything in Julia has a type, including types themselves, which are of type DataType: typeof(Int64) returns DataType. Conversion of a variable var to a type Type1 can be done using the type name as a function, such as Type1(var). For example, Int64(3.0) returns 3.

However, this raises an error if type conversion is impossible, as follows:

julia> Int64("hello") 
ERROR: MethodError: Cannot `convert` an object of type String to an object of type Int64

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.71.115