Image for post
Image for post

We provide an overview of basic Julia functionality. The source code for this document is available on GitHub. Some notable concepts not covered in this guide include the type system, variable scopes, array broadcasting, module imports, and exception handling.

Printing output

Use print to print a canonical text representation of your input. You will often want to use println, which appends a newline \n to your output.

Comments

Use # for an inline comment. Use #= and =# for multiline comments.

Strings

Strings are denoted via single quotes " or triple quotes """, which differ in indentation behavior.

Contrary to many other languages, Julia uses * instead of + for string concatenation. The number of characters in a string S is retrievable via length(S).

Numbers

Below is the type tree of the subtypes of Number.

Julia will use types Int64 and Float64 for default numerical inputs.

Standard binary operators will convert input and output types in the way you would expect.

Variables

We have a rather high degree of freedom for variable names. We can assign values to a variable via =. We refer to the official documentation for a specification of allowed variable names.

We may insert variable values in a template-like manner into strings via $( ).

Control flow

Generally, control structures end with an end keyword. Below are some examples for if-else statements. We may use the elseif keyword to abbreviate else (if ... end).

While-loops use a similar syntax. We may use the break keyword to break out of a control structure.

For-loops iterate over iterable values which we will explain more thoroughly in a later section. The syntaxes for a = A, for a ∈ A and for a in A are equivalent. The iteration reference documentation explains how for-loops are translated to while-loops internally.

The continue keyword lets us skip to the next iteration of a loop.

Functions

We may define functions via assignment or by using the function keyword. When using the function keyword, the last statement will be the function return value. The return keyword lets us return a value mid-function. The input and output types of a function can be specified via ::. The following function definitions are all equivalent.

Functions will return the last executed statement. If we do not want a function to return a value we need to return nothing. We may use just the keyword nothing, just the keyword return, or the combination of both.

It is possible to compose functions via the operator, where (f ∘ g)(x) = f(g(x)). Note that function compositions are generally noncommutative.

Omitting type constraints is equivalent to enforcing the Any type. It is a good idea to find a good balance between generality and specificity for type constraints:

  • Type constraints provide a way of intrinsic documentation which may reduce the time it takes collaborators to understand our code.
  • Overly complicated type constraints may hinder readability and increase the time it takes collaborators to understand our code.
  • Ambiguous function definitions may cause bugs. E.g. consider a function using the * operator which multiplies numbers and concatenates strings. If such a function is not type constrained it will not raise an error, even if we intended the function to only work with numerical inputs.
  • Very specific type constraints may force us to unnecessarily redefine the same function multiple times to cover all relevant cases.
  • Counterintuitively, in many common cases, there is no performance penalty for using less specific types, e.g. for Integer and Int64.
  • In some cases, e.g. when working with arrays, not type-constraining a function precisely enough may impact performance (see this reference).

We should point out that function definitions just add methods to a function object. If we define a function several times with different type constraints we will just add more methods to the function, not overwriting or only partly overwriting previous definitions. This can lead to confusing behavior when writing e.g. a Jupyter notebook where we tend to reuse variable names often.

Documentation

Julia docstrings implement the Markdown standard. Docstrings may be written directly above functions and types, but also above macros (which we will not cover in an introduction to Julia) and instantiated values (e.g. constants we defined). We use Julia’s string syntax for docstrings. Below are examples of docstrings which adhere to the official docstring recommendations.

Arrays

Arrays are part of the Julia standard library. Arrays are of type Array{T, N}, where T is a supertype of the smallest common type of the array's entries and N is the number of array dimensions. Note that we use display instead of println to pretty-print the defined arrays. Type enforced arrays of type Array{T, 1} may be created via the syntax T[A, B, C, ...]. If T is omitted, the array will fall back to the smallest common type of its entries which is NOT necessarily the Any type (contrary to the way this was handled in function definitions).

Semicolons ; and newlines concatenate entries vertically. Spaces concatenate entries horizontally.

Do not confuse commata and concatenation:

  • An array defined via [A, B, C] will be of type Array{T, 1}, where T is the smallest common element type of A, B, and C as described above.
  • The syntax [A; B; C] is shorthand for vcat(A, B, C), i.e the entries A, B, and C will be concatenated along their first dimension.
  • The syntax [A B C] is shorthand for hcat(A, B, C), i.e. the entries A, B, and C will be concatenated along their second dimension.

Imagining elements of type UnitRange as vectors similar to 1-dimensional arrays provides intuition for concatenation behavior.

Julia comes with a number of functions to instantiate arrays such as zeros for an array filled with zeros and rand for an array filled with entries uniformly distributed in the interval [0, 1]. We refer to the official documentation for a comprehensive list of array instantiation functions.

Indexing

The general syntax for indexing into an n-dimensional array is A[I_1, ..., I_n], where I_i is a supported index. Supported indices include, but are not limited to

  • a scalar index for a specific index in the corresponding dimension,
  • a UnitRange for multiple indices in the corresponding dimension,
  • a colon : for all indices in a corresponding dimension.

See the official documentation for a comprehensive overview of supported index types.

When indexing into strings via single integer, an element of type Character will be returned, not an element of type String. However, strings are encoded via the UTF-8 encoding and integer indices refer to the corresponding underlying bytes of the encoded characters. Since the UTF-8 encoding of a Character may consist of multiple bytes, not every integer index is necessarily a valid index into a String. The valid integer indices are those referring to the first byte of encoded characters.

The first and last index may be indexed via begin and end, respectively.

When using a UnitRange as an index into a string a new element of type String is returned, not a view of the original string and not a character, even if the UnitRange contains a single element.

Iteration

We may iterate over an array with for-loops. If we want to iterate over the valid indices of an array A, we iterate over eachindex(A). Note that arrays are stored in column-major order, so the array below is printed as 3546, not as 3456.

Strings behave similarly but iterating over a string iterates over the valid characters, not the byte indices. We illustrate this point by using the eachindex method.

Written by

We develop artificial intelligence applications for eomii.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store