Functions in Julia
Learn how to define and use functions in Julia - parameters, return values, default and keyword arguments, recursion, higher-order functions, and multiple dispatch
Functions are the heart of Julia. Where many languages treat functions as just a way to organize code, Julia builds its entire programming model around them through multiple dispatch—a function can have many methods, and Julia chooses which one to run based on the types of all the arguments.
This makes functions in Julia unusually expressive. You can write generic code that works across types, extend other people’s functions without inheritance, and compose small functions into fast, readable programs. As a multi-paradigm language, Julia treats functions as first-class values: you can pass them as arguments, return them from other functions, and store them in variables.
In this tutorial you will learn the different ways to define functions, how to pass and return values, how scope works, how to write recursive functions, and how higher-order functions and multiple dispatch shape idiomatic Julia code.
Defining Functions
Julia offers several ways to define a function. The function ... end block is the most general form, while the assignment form is a compact shorthand for one-liners.
Create a file named functions.jl:
| |
Notice that return is optional: a function returns the value of its last expression. The assignment form square(x) = x^2 is exactly equivalent to a function block—it is not a lesser kind of function.
Recursion
Julia supports recursion directly, and the ternary operator cond ? a : b makes base cases concise. Recursion is the natural way to express many mathematical definitions.
Create a file named recursion.jl:
| |
The comprehension [fib(n) for n in 0:9] calls fib for each value in the range 0:9 and collects the results into an array—a compact, functional way to build sequences.
Higher-Order Functions
Because functions are first-class values, you can pass them around and return them. Julia’s map, filter, and reduce, combined with anonymous functions (x -> ...), are the workhorses of functional-style Julia.
Create a file named higher_order.jl:
| |
The do block is syntactic sugar: map([1, 2, 3]) do x ... end is the same as map(x -> ..., [1, 2, 3]), but it lets you write a multi-line function body cleanly. The multiplier function returns a closure that “remembers” the factor it was created with.
Multiple Dispatch
Multiple dispatch is Julia’s defining feature. A single function name can have many methods, each specialized for different argument types. Julia picks the most specific matching method at call time, based on the types of all arguments—not just the first.
Create a file named dispatch.jl:
| |
This is fundamentally different from object-oriented method dispatch, where the method is chosen by a single receiver object. Here, collide(7, "hello") selects the (Int, String) method by examining both arguments. The untyped describe_num(x) method acts as a fallback for any type not matched more specifically.
Variable Scope
Functions introduce a new local scope. Assigning to a variable inside a function creates a new local variable by default—it does not modify a global of the same name unless you explicitly say global.
Create a file named scope.jl:
| |
This default protects you from accidentally clobbering globals. In practice, idiomatic Julia avoids mutable globals entirely—passing values as arguments and returning results keeps functions pure and fast.
Running with Docker
You can run each example using the official Julia image without installing anything locally:
| |
Expected Output
Running functions.jl:
add(3, 4) = 7
multiply(3, 4) = 12
square(5) = 25
Hello, Julia!
Hi, Julia!
Hello, Julia?
lo = 1, hi = 7
total(1, 2, 3, 4) = 10
Running recursion.jl:
5! = 120
10! = 3628800
fib(10) = 55
First 10 Fibonacci: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
Running higher_order.jl:
double(21) = 42
squared = [1, 4, 9, 16, 25, 36]
evens = [2, 4, 6]
sum = 21
apply_twice(double, 5) = 20
triple(10) = 30
do-block result = [2, 5, 10]
Running dispatch.jl:
two integers: 3
two strings: ab
int and string: 7 and hello
Woof!
Meow!
Methods of collide: 3
42 is an integer
3.14 is a float
text is something else
Running scope.jl:
Local result: 5
Global counter: 0
After increment_global: 1
Global counter now: 1
compute() = 200
Key Concepts
- Two definition forms — Use
function ... endfor general functions and the compactf(x) = ...assignment form for one-liners; they are fully equivalent. - Implicit return — A function returns the value of its last expression;
returnis only needed to exit early. - Flexible arguments — Julia supports default positional arguments, keyword arguments (after
;), and varargs (args...) in a single signature. - Multiple return values — Return a tuple and destructure it with
lo, hi = min_max(xs). - First-class functions — Functions can be passed as arguments, returned as closures, and used with
map,filter, andreduce; anonymous functions usex -> .... - Multiple dispatch — A function name can have many methods selected by the types of all arguments, enabling generic, extensible code without inheritance.
- Scope safety — Assignment inside a function creates a local variable by default; use
globalto modify a global explicitly. - The do-block —
f(args) do x ... endis a clean way to pass a multi-line function as the first argument.
Comments
Loading comments...
Leave a Comment