Zoom Functions

Last updated: January 12, 2023

 Table of contents

Functions are objects containing a set of instructions.
When you pass a tuple of argument(s) (possibly an empty tuple) to them, you get one or more values as output.

Operators

Operators are functions and can be written in a way that shows the tuple of arguments more explicitly:

3 + 2 == +(3, 2)  # `==` tests for equality
true
+(3, 2)
5

Function definition

There are 2 ways to define a new function:

Long form

function <name>(<arguments>)
    <body>
end
 Example:
function hello1()
    println("Hello")
end
hello1 (generic function with 1 method)

Assignment form

<name>(<arguments>) = <body>
 Example:
hello1() = println("Hello")
hello1 (generic function with 1 method)

The function hello1 defined with this compact syntax is exactly the same as the one we defined above.

Stylistic convention

Julia suggests to use lower case, without underscores, as function names.

Calling functions

Since you pass a tuple to a function when you run it, you call a function by appending parentheses to its name:

hello1()
Hello
Here, our function does not take any argument, so the tuple is empty.

Arguments

No argument

Our function hello does not accept any argument. If we pass an argument, we get an error message:

hello1("Bob")
LoadError: MethodError: no method matching hello1(::String)
Closest candidates are:
  hello1() at In[5]:1

One argument

To define a function which accepts an argument, we need to add a placeholder for it in the function definition.

 So let’s try this:
function hello2(name)
    println("Hello name")
end
hello2 (generic function with 1 method)
hello2("Bob")
Hello name

Mmm … not quite … this function works but does not give the result we wanted.

Here, we need to use string interpolation:

function hello3(name)
    println("Hello $name")
end
hello3 (generic function with 1 method)

$name in the body of the function points to name in the tuple of argument.

When we run the function, $name is replaced by the value we used in lieu of name in the function definition:

hello3("Bob")
Hello Bob
 Note that this dollar sign is only required with strings. Here is an example with integers:
function cube(a)
    a ^ 3
end

cube(4)
64

Multiple arguments

Now, let’s write a function which accepts 2 arguments. For this, we put 2 placeholders in the tuple passed to the function in the function definition:

function hello4(name1, name2)
    println("Hello $name1 and $name2")
end
hello4 (generic function with 1 method)

This means that this function expects a tuple of 2 values:

hello4("Bob", "Pete")
Hello Bob and Pete
See what happens when you pass no argument, a single argument, or three arguments to this function.

Default arguments

You can set a default value for some or all arguments. In this case, the function will run with or without a value passed for those arguments. If no value is given, the default is used. If a value is given, it will replace the default.

 Here is an example:
function hello5(name="")
    println("Hello $name")
end
hello5 (generic function with 2 methods)
hello5()
Hello 
hello5("Bob")
Hello Bob

Returning the result

In Julia, functions return the value(s) of the last expression automatically.
If you want to return something else instead, you need to use the return statement. This causes the function to exit early.

 Look at these 5 functions:
function test1(x, y)
    x + y
end

function test2(x, y)
    return x + y
end

function test3(x, y)
    x * y
    x + y
end

function test4(x, y)
    return x * y
    x + y
end

function test5(x, y)
    return x * y
    return x + y
end

function test6(x, y)
    x * y, x + y
end
Without running the code, try to guess the outputs of:
test1(1, 2)
test2(1, 2)
test3(1, 2)
test4(1, 2)
test5(1, 2)
test6(1, 2)
Now, run the code and draw some conclusions on the behaviour of the return statement.

Anonymous functions

Anonymous functions are functions which aren’t given a name:

function (<arguments>)
    <body>
end

In compact form:

<arguments> -> <body>
 Example:
function (name)
    println("Hello $name")
end
#1 (generic function with 1 method)
 Compact form:
name -> println("Hello $name")
#3 (generic function with 1 method)

When would you want to use anonymous functions?

This is very useful for functional programming (when you apply a function—for instance map—to other functions to apply them in a vectorized manner which avoids repetitions).

 Example:
map(name -> println("Hello $name"), ["Bob", "Lucie", "Sophie"]);
Hello Bob
Hello Lucie
Hello Sophie

Pipes

|> is the pipe in Julia.
It redirects the output of the expression on the left as the input of the expression on the right.

 The following 2 expressions are equivalent:
println("Hello")
"Hello" |> println
 Here is another example:
sqrt(2) == 2 |> sqrt
true

Function composition

You can pass a function inside another function:

<function2>(<function1>(<arguments>))

<arguments> will be passed to <function1> and the result will then be passed to <function2>.

An equivalent syntax is to use the composition operator โˆ˜ (in the REPL, type \circ then press tab):

(<function2> โˆ˜ <function1>)(<arguments>)
 Example:
# sum is our first function
sum(1:3)
6
# sqrt is the second function
sqrt(sum(1:3))
2.449489742783178
# This is equivalent
(sqrt โˆ˜ sum)(1:3)
2.449489742783178
Write three other equivalent expressions using the pipe.

Mutating functions

Functions usually do not modify their argument(s):

a = [-2, 3, -5]
3-element Vector{Int64}:
 -2
  3
 -5
sort(a)
3-element Vector{Int64}:
 -5
 -2
  3
a
3-element Vector{Int64}:
 -2
  3
 -5

Julia has a set of functions which modify their argument(s). By convention, their names end with !

 The function sort has a mutating equivalent sort!:
sort!(a);
a
3-element Vector{Int64}:
 -5
 -2
  3
If you write functions which modify their arguments, make sure to follow this convention too.

Broadcasting

To apply a function to each element of a collection rather than to the collection as a whole, Julia uses broadcasting.

 Let’s create a collection (here a tuple):
a = (2, 3)
(2, 3)
 If we pass a to the string function, that function applies to the whole collection:
string(a)
"(2, 3)"
 In contrast, we can broadcast the function string to all elements of a:
broadcast(string, a)
("2", "3")
 An alternative syntax is to add a period after the function name:
string.(a)
("2", "3")

We will see more examples in the array section.

Comments & questions