Style Guide The Julia Language
Style Guide The Julia Language
Style Guide
The following sections explain a few aspects of idiomatic Julia coding style. None of these rules are
absolute; they are only suggestions to help familiarize you with the language and to help you choose among
alternative designs.
It is also worth emphasizing that functions should take arguments, instead of operating directly on global
variables (aside from constants like pi ).
Complex{Float64}(x)
complex(float(x))
The second version will convert x to an appropriate type, instead of always the same type.
This style point is especially relevant to function arguments. For example, don't declare an argument to be
of type Int or Int32 if it really could be any integer, expressed with the abstract type Integer . In fact, in
many cases you can omit the argument type altogether, unless it is needed to disambiguate from other
method de nitions, since a MethodError will be thrown anyway if a type is passed that does not support
any of the requisite operations. (This is known as duck typing.)
For example, consider the following de nitions of a function addone that returns one plus its argument:
https://docs.julialang.org/en/v1/manual/style-guide/ 1/9
5/6/2020 Style Guide · The Julia Language
The last de nition of addone handles any type supporting oneunit (which returns 1 in the same type as x ,
which avoids unwanted type promotion) and the + function with those arguments. The key thing to realize
is that there is no performance penalty to de ning only the general addone(x) = x + oneunit(x) ,
because Julia will automatically compile specialized versions as needed. For example, the rst time you call
addone(12) , Julia will automatically compile a specialized addone function for x::Int arguments, with
the call to oneunit replaced by its inlined value 1 . Therefore, the rst three de nitions of addone above
are completely redundant with the fourth de nition.
function foo(x, y)
x = Int(x); y = Int(y)
...
end
foo(x, y)
use:
This is better style because foo does not really accept numbers of all types; it really needs Int s.
One issue here is that if a function inherently requires integers, it might be better to force the caller to
decide how non-integers should be converted (e.g. oor or ceiling). Another issue is that declaring more
speci c types leaves more "space" for future method de nitions.
https://docs.julialang.org/en/v1/manual/style-guide/ 2/9
5/6/2020 Style Guide · The Julia Language
function double(a::AbstractArray{<:Number})
for i = firstindex(a):lastindex(a)
a[i] *= 2
end
return a
end
use:
function double!(a::AbstractArray{<:Number})
for i = firstindex(a):lastindex(a)
a[i] *= 2
end
return a
end
Julia Base uses this convention throughout and contains examples of functions with both copying and
modifying forms (e.g., sort and sort! ), and others which are just modifying (e.g., push! , pop! , splice! ). It
is typical for such functions to also return the modi ed array for convenience.
a = Vector{Union{Int,AbstractString,Tuple,Array}}(undef, n)
In this case Vector{Any}(undef, n) is better. It is also more helpful to the compiler to annotate speci c
uses (e.g. a[i]::Int ) than to try to pack many alternatives into one type.
functions are lowercase ( maximum , convert ) and, when readable, with multiple words squashed
together ( isequal , haskey ). When necessary, use underscores as word separators. Underscores are
https://docs.julialang.org/en/v1/manual/style-guide/ 3/9
5/6/2020 Style Guide · The Julia Language
If a function name requires multiple words, consider whether it might represent more than one concept
and might be better split into pieces.
1. Function argument. Putting a function argument rst permits the use of do blocks for passing
multiline anonymous functions.
2. I/O stream. Specifying the IO object rst permits passing the function to functions such as sprint , e.g.
sprint(show, x) .
3. Input being mutated. For example, in fill!(x, v) , x is the object being mutated and it appears
before the value to be inserted into x .
4. Type. Passing a type typically means that the output will have the given type. In parse(Int, "1") , the
type comes before the string to parse. There are many such examples where the type appears rst, but
it's useful to note that in read(io, String) , the IO argument appears before the type, which is in
keeping with the order outlined here.
5. Input not being mutated. In fill!(x, v) , v is not being mutated and it comes after x .
6. Key. For associative collections, this is the key of the key-value pair(s). For other indexed collections,
this is the index.
7. Value. For associative collections, this is the value of the key-value pair(s). In cases like fill!(x, v) ,
this is v .
8. Everything else. Any other arguments.
9. Varargs. This refers to arguments that can be listed inde nitely at the end of a function call. For
example, in Matrix{T}(undef, dims) , the dimensions can be given as a Tuple , e.g. Matrix{T}
(undef, (1,2)) , or as Vararg s, e.g. Matrix{T}(undef, 1, 2) .
10. Keyword arguments. In Julia keyword arguments have to come last anyway in function de nitions;
they're listed here for the sake of completeness.
The vast majority of functions will not take every kind of argument listed above; the numbers merely
denote the precedence that should be used for any applicable arguments to a function.
https://docs.julialang.org/en/v1/manual/style-guide/ 4/9
5/6/2020 Style Guide · The Julia Language
There are of course a few exceptions. For example, in convert , the type should always come rst. In
setindex! , the value comes before the indices so that the indices can be provided as varargs.
When designing APIs, adhering to this general order as much as possible is likely to give users of your
functions a more consistent experience.
if a == b
instead of:
if (a == b)
foo(x::Real) = ...
instead, especially if T is not used in the function body. Even if T is used, it can be replaced with typeof(x)
if convenient. There is no performance difference. Note that this is not a general caution against static
parameters, just against uses where they are not needed.
https://docs.julialang.org/en/v1/manual/style-guide/ 5/9
5/6/2020 Style Guide · The Julia Language
Note also that container types, speci cally may need type parameters in function calls. See the FAQ Avoid
elds with abstract containers for more information.
foo(::Type{MyType}) = ...
foo(::MyType) = foo(MyType)
Decide whether the concept in question will be written as MyType or MyType() , and stick to it.
The preferred style is to use instances by default, and only add methods involving Type{MyType} later if
they become necessary to solve some problem.
Calling eval inside a macro is a particularly dangerous warning sign; it means the macro will only work
when called at the top level. If such a macro is written as a function instead, it will naturally have access to
the run-time values it needs.
https://docs.julialang.org/en/v1/manual/style-guide/ 6/9
5/6/2020 Style Guide · The Julia Language
getindex(x::NativeType, i) = unsafe_load(x.p, i)
The problem is that users of this type can write x[i] without realizing that the operation is unsafe, and
then be susceptible to memory bugs.
Such a function should either check the operation to ensure it is safe, or have unsafe somewhere in its
name to alert callers.
This would provide custom showing of vectors with a speci c new element type. While tempting, this
should be avoided. The trouble is that users will expect a well-known type like Vector() to behave in a
certain way, and overly customizing its behavior can make it harder to work with.
module A
import Base.*
*(x::Symbol, y::Symbol) = Symbol(x,y)
end
The problem is that now any other module that uses Base.* will also see this de nition. Since Symbol is
de ned in Base and is used by other modules, this can change the behavior of unrelated code unexpectedly.
There are several alternatives here, including using a different function name, or wrapping the Symbol s in
another type that you de ne.
Sometimes, coupled packages may engage in type piracy to separate features from de nitions, especially
when the packages were designed by collaborating authors, and when the de nitions are reusable. For
https://docs.julialang.org/en/v1/manual/style-guide/ 7/9
5/6/2020 Style Guide · The Julia Language
example, one package might provide some types useful for working with colors; another package could
de ne methods for those types that enable conversions between color spaces. Another example might be a
package that acts as a thin wrapper for some C code, which another package might then pirate to
implement a higher-level, Julia-friendly API.
For example,
julia> f(1//2)
1.0
julia> f(1/2)
1.0
julia> f(1)
2.0
while
https://docs.julialang.org/en/v1/manual/style-guide/ 8/9
5/6/2020 Style Guide · The Julia Language
julia> g(x) = 2 * x
g (generic function with 1 method)
julia> g(1//2)
1//1
julia> g(1/2)
1.0
julia> g(1)
2
As you can see, the second version, where we used an Int literal, preserved the type of the input
argument, while the rst didn't. This is because e.g. promote_type(Int, Float64) == Float64 , and
promotion happens with the multiplication. Similarly, Rational literals are less type disruptive than
Float64 literals, but more disruptive than Int s:
julia> h(1//2)
1//1
julia> h(1/2)
1.0
julia> h(1)
2//1
Thus, use Int literals when possible, with Rational{Int} for literal non-integer numbers, in order to make
it easier to use your code.
https://docs.julialang.org/en/v1/manual/style-guide/ 9/9