Julia Module 3 – Control flow, functions and scope
Functions, arguments and keyword arguments
Write Julia functions in long, short and anonymous forms, with defaults, required keywords, varargs, splatting, tuple returns and in-place versions.
What you will learn
- Define functions in long, short and anonymous forms, including do-blocks
- Use optional, keyword, required-keyword and variadic arguments and splatting
- Annotate argument types for dispatch without shutting out Float32 or BigFloat
- Use tuples to return several values, and destructure them
- Follow the ! convention for functions that mutate their arguments
Before you start
What you’ll learn · Before you start
What you will learn
- Define functions in long, short and anonymous forms, including do-blocks
- Use optional, keyword, required-keyword and variadic arguments and splatting
- Annotate argument types for dispatch without shutting out Float32 or BigFloat
- Use tuples to return several values, and destructure them
- Follow the ! convention for functions that mutate their arguments
Before you start
On this page
A call such as deflection(5000, 2.0, 200e9, 8.356e-5) computes the tip deflection of a steel cantilever, but nothing
in it says which number is the load, the length, the stiffness of the steel or the shape of the beam. Name the last two,
give the steel’s stiffness as a default, and the same call reads deflection(5000, 2.0; I = 8.356e-5): 0.8 mm for an
IPE 300 beam 2 m long with 5 kN at its tip.
This lesson builds that function and its neighbours, recorded on Julia 1.13.1. You will write functions in their three forms and with do-blocks, give arguments defaults, require keywords, accept any number of arguments, return several values at once, and name the functions that change their arguments so that every caller can tell.
A function can be written three ways, and a do-block passes one
The same function, the area of a rectangle, can be written in three forms. The long form uses function … end and may
hold any number of lines; the short form, area_short(w, h) = w * h, fits one expression; an anonymous function,
(w, h) -> w * h, has no name of its own and is usually passed straight to another function:
# One function written three ways, then a do-block, which passes a function as the first argument of a call.
function area_long(w, h)
return w * h
end
area_short(w, h) = w * h
area_anon = (w, h) -> w * h
println(area_long(3, 4), " ", area_short(3, 4), " ", area_anon(3, 4))
spans = [2.0, 3.5, 1.25]
println(map(x -> 2x, spans))
doubled = map(spans) do x
2x
end
println(doubled)
open("spans.txt", "w") do io # open closes the file when the block ends, even after an error
for s in spans
println(io, s, " m")
end
end
println(readlines("spans.txt")) from session import files, run
# The long, short and anonymous forms of one function, then two do-blocks.
files("forms.jl")
run("""
julia forms.jl
""") Output
$ julia forms.jl 12 12 12 [4.0, 7.0, 2.5] [4.0, 7.0, 2.5] ["2.0 m", "3.5 m", "1.25 m"]
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 forms_session.py
All three printed 12. A function returns the value of its last expression unless a return ends it earlier, so the
long form’s return is a choice of style; it makes the result easy to find in a function of many lines.
The second half of the program is the reason anonymous functions are everywhere in Julia. map(x -> 2x, spans) hands
the function to map, which calls it on every item. When that function needs several lines, a
do-block: Syntax that writes a multi-line anonymous function after a call and passes it as the call's first argument: map(v) do x … end is map(x -> …, v). writes it after the call instead. Step through what Julia makes of the two blocks:
The drawing shows the whole scene; its 5 steps are listed below.
Every step as text
- The block. A do-block follows a call: map(spans) do x. The name after do, x, is the parameter of a function that has no name.
- Its body. The lines up to the matching end are that function's body. Here the body is 2x, so the function doubles its argument.
- An anonymous function. Julia turns the block into the anonymous function x -> 2x.
- Passed first. It passes that function as the first argument of the call, ahead of spans: map(x -> 2x, spans). Both forms printed [4.0, 7.0, 2.5].
- The same for open. open(path, "w") do io … end is the same rewrite. open receives a function of io, opens the file, calls the function and closes the file when it returns, even after an error.
The open block is the pattern you will use most: the file is closed when the block ends, which the manual says
happens whether the function exits normally or throws an exception. The run read the file back as three lines.
return inside a do-block
Wrong: return inside a do-block to leave the function that contains it. The block is a function of its own, so
return leaves only the block and the outer function goes on. Right: compute a value in the block and return it
from the outer function after the call, or use a loop where an early exit is the point.
A default value adds a method, and a type annotation narrows it
area(w, h = w) gives h a default, so the function can be called with one argument or two. The manual explains how:
optional arguments are only a convenient syntax for writing several method definitions. Each
method: One definition of a function for some types of arguments. A Julia function can have many methods, and each call runs the most specific one that fits the argument types. More in “What Julia is for, and when to choose it” is one definition for one way of calling the function, and methods(area) lists them:
# A default value for a positional argument: Julia writes one method per way of calling the function.
area(w, h = w) = w * h # a rectangle, or a square when h is left out
println(area(3, 4), " ", area(3))
println(methods(area))
half_narrow(x::Float64) = x / 2 # accepts only Float64
half(x::Real) = x / 2 # accepts every real number
println(half(1.5), " ", half(1.5f0), " ", half(big"1.5"), " ", half(3))
println(typeof(half(1.5f0)), " ", typeof(half(big"1.5")))
half_narrow(1.5f0) from session import files, run
# A default value makes two methods; a narrow type annotation shuts out a Float32.
files("optional.jl")
run("""
!julia optional.jl
""") Output
$ julia optional.jl
12 9
# 2 methods for generic function "area" from Main:
[1] area(w, h)
@ ~/demo/optional.jl:2
[2] area(w)
@ ~/demo/optional.jl:2
0.75 0.75 0.75 1.5
Float32 BigFloat
ERROR: LoadError: MethodError: no method matching half_narrow(::Float32)
The function `half_narrow` exists, but no method is defined for this combination of argument types.
Closest candidates are:
half_narrow(!Matched::Float64)
@ Main ~/demo/optional.jl:6
Stacktrace:
[1] top-level scope
@ ~/demo/optional.jl:10
[2] include(mod::Module, _path::String)
@ Base ./Base.jl:309
[3] exec_options(opts::Base.JLOptions)
@ Base ./client.jl:344
[4] _start()
@ Base ./client.jl:577
in expression starting at ~/demo/optional.jl:10
[exit status 1]
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 optional_session.py
area(3, 4) gave 12 and area(3) gave 9, the area of a square, and methods(area) listed two methods, area(w, h)
and area(w). The one-argument method simply calls the other with h = w. An
optional argument: A positional argument with a default value, as h in area(w, h = w). It is still matched by position, and Julia writes one method for each number of arguments the function accepts. is still matched by position: to give h, you must give w
first.
The second half annotates argument types. half_narrow(x::Float64) accepts only a Float64, so the last line, with
the Float32 value 1.5f0, stopped with MethodError: no method matching half_narrow(::Float32), and the message
listed the closest candidate, the method that did not match. half(x::Real) accepted a Float64, a Float32, a
BigFloat and an Int, and kept the type of each float: half(1.5f0) is a Float32 and half(big"1.5") a
BigFloat, while half(3) gave the Float64 1.5, because / on integers returns a float.
Narrow annotations buy no speed. The manual says that argument-type declarations normally have no impact on performance, because Julia compiles a specialised version of a function for the concrete types it is called with, annotated or not. Annotations are for choosing a method, for documentation and for refusing input that would give nonsense; the style guide warns against overly specific types, and the manual’s advice is “when in doubt, omit the argument types”.
Checkpoint
Check: Function forms and do-blocks · Defaults, methods and type annotations
Keywords name the arguments, and one without a default is required
The tip of a cantilever with a load F at its unsupported end deflects by F·L³ / (3·E·I), the formula the beam deflection
calculator uses too, where L is the length, E the stiffness of the material (Young’s modulus) and I the second moment
of area of the beam’s section. E is the same for
every beam of one material, so the function gives it a default, 200 GPa for steel, while I depends on the shape of the
section and has none; the examples use 8.356e-5 m⁴, the value the moment of inertia calculator gives for an IPE 300.
So deflection takes F and L by position and E and I as keyword argument: An argument declared after a semicolon and passed by name, as I in deflection(F, L; E = 200e9, I). A keyword without a default must be given, or the call throws UndefKeywordError., after a
semicolon, with a default for E only:
"""
deflection(F, L; E = 200e9, I)
Tip deflection, in metres, of a cantilever of length `L` metres with a load of `F` newtons at its free end:
F * L^3 / (3 * E * I). `E` is Young's modulus in pascals (200 GPa, steel, unless given) and `I` the second moment of
area in m^4, which has no default.
"""
deflection(F, L; E = 200e9, I) = F * L^3 / (3 * E * I) # Keyword arguments: E has a default, I has none, so every call must name I.
include("deflection.jl")
I_ipe300 = 8.356e-5 # an IPE 300 steel section about its strong axis: 8,356 cm^4
mm(x) = round(x * 1000; digits = 2)
println("steel: ", mm(deflection(5_000, 2.0; I = I_ipe300)), " mm")
println("E = 70 GPa: ", mm(deflection(5_000, 2.0; E = 70e9, I = I_ipe300)), " mm")
println("any order: ", deflection(5_000, 2.0; I = I_ipe300, E = 200e9) == deflection(5_000, 2.0; I = I_ipe300))
I = I_ipe300
println("shorthand: ", mm(deflection(5_000, 2.0; I)), " mm") # ; I means I = I
deflection(5_000, 2.0) Follow the first call as Julia binds its arguments:
The drawing shows the whole scene; its 7 steps are listed below.
Every step as text
- The call. deflection(5_000, 2.0; I = I_ipe300) passes two arguments by position and one by name. The semicolon separates them, and E is not mentioned at all.
- By position. Positional arguments are matched by their order: F = 5000 and L = 2.0. Swapped, they would still run and give a wrong number.
- A default. E was not given, so its default, 200e9 pascals for steel, is used. A default is evaluated only for a keyword the call leaves out.
- By name. I is matched by its name, wherever it stands among the keywords. It has no default, so every call must give it.
- The body. With all four bound, the body computes 5000 × 2.0³ / (3 × 200e9 × 8.356e-5).
- The result. The answer is 7.98e-4 m, which the run printed as 0.8 mm. The call with E = 70e9 gave 2.28 mm.
- Without I. A call that leaves I out stops before the body runs: UndefKeywordError: keyword argument
Inot assigned, as the last line of the run shows.
Here is what the run of cantilever.jl printed, line by line:
from session import files, run
# Keyword arguments with a default and a required one, called four ways and then without the required keyword.
files("deflection.jl", "cantilever.jl")
run("""
!julia cantilever.jl
""") Output
$ julia cantilever.jl steel: 0.8 mm E = 70 GPa: 2.28 mm any order: true shorthand: 0.8 mm ERROR: LoadError: UndefKeywordError: keyword argument `I` not assigned Stacktrace: [1] deflection(F::Int64, L::Float64) @ Main ~/demo/deflection.jl:8 [2] top-level scope @ ~/demo/cantilever.jl:11 [3] include(mod::Module, _path::String) @ Base ./Base.jl:309 [4] exec_options(opts::Base.JLOptions) @ Base ./client.jl:344 [5] _start() @ Base ./client.jl:577 in expression starting at ~/demo/cantilever.jl:11 [exit status 1]
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 cantilever_session.py
Steel gave 0.8 mm, and a softer material with E = 70e9 gave 2.28 mm. The keywords could come in any order, and
; I was short for I = I, the variable of the same name. The last call left I out and stopped with
UndefKeywordError: keyword argument `I` not assigned: a keyword without a default is required, and Julia says
which one is missing.
The text in triple quotes above the definition is a docstring. It must sit directly above the definition, with
nothing between them, and its first line is the signature, indented by four spaces. Typing ? at the REPL prompt
turns it into help mode, which shows it:
from session import files, repl
# The docstring above deflection, shown by the REPL's help mode.
files("deflection.jl")
repl([
'include("deflection.jl")',
"?deflection",
]) Output
$ julia --startup-file=no -q
julia> include("deflection.jl")
deflection (generic function with 1 method)
help?> deflection
search: deflection function @cfunction selectdim delete! setprecision
deflection(F, L; E = 200e9, I)
Tip deflection, in metres, of a cantilever of length L metres with a load of
F newtons at its free end: F * L^3 / (3 * E * I). E is Young's modulus in
pascals (200 GPa, steel, unless given) and I the second moment of area in
m^4, which has no default.
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 docstring_session.py
Varargs gather arguments, and splatting spreads them
A function written total(xs...) accepts any number of arguments: the manual calls them
varargs: A function that accepts any number of arguments, written with ... after the last parameter, as total(xs...). Inside the function, xs is a tuple of the arguments passed, empty when there are none., and inside the function xs is a tuple of whatever was passed, empty when nothing
was. ... in a call does the opposite, splatting: Writing ... after a collection in a call, as total(v...), so that its elements are passed as separate arguments instead of one.: total([4, 5]...) spreads the vector
into two separate arguments.
# xs... collects any number of arguments into a tuple; v... in a call spreads a collection into arguments.
total(xs...) = sum(xs; init = 0)
println(total(1, 2, 3), " ", total(), " ", total([4, 5]...))
total_no_init(xs...) = sum(xs) # the same without init
try
total_no_init()
catch e
println("total_no_init(): ", sprint(showerror, e))
end
describe(first, rest...) = println("first ", first, ", then ", length(rest), " more: ", rest)
describe(10, 20, 30)
include("deflection.jl")
# Every load, the same keywords: kwargs... collects them, and ; kwargs... passes them on.
deflections(loads...; kwargs...) = [round(deflection(F, 2.0; kwargs...) * 1000; digits = 2) for F in loads]
println(deflections(1_000, 5_000, 10_000; I = 8.356e-5), " mm")
println(deflections(5_000; E = 70e9, I = 8.356e-5), " mm") from session import files, run
# Varargs and splatting, a sum of no arguments without init, then keywords collected with kwargs... and passed on.
files("deflection.jl", "varargs.jl")
run("""
julia varargs.jl
""") Output
$ julia varargs.jl 6 0 9 total_no_init(): ArgumentError: reducing over an empty collection is not allowed; consider supplying `init` to the reducer first 10, then 2 more: (20, 30) [0.16, 0.8, 1.6] mm [2.28] mm
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 varargs_session.py
What each call of the run received:
| Call | Inside the function | Result |
|---|---|---|
total(1, 2, 3) |
xs = (1, 2, 3) |
6 |
total() |
xs = () |
0, from init = 0 |
total([4, 5]...) |
xs = (4, 5) |
9 |
total_no_init() |
xs = () |
ArgumentError |
describe(10, 20, 30) |
first = 10, rest = (20, 30) |
“2 more” |
total() needed sum(xs; init = 0): an empty tuple has no first value to start a sum from, and total_no_init(),
the same function without init, threw ArgumentError: reducing over an empty collection is not allowed. Keywords can be
gathered the same way: deflections(loads...; kwargs...) collects every keyword it is given and passes them all on
with ; kwargs..., so one I served three loads, 0.16, 0.8 and 1.6 mm, and E = 70e9 reached deflection without
deflections knowing about it. This is how a function wraps another without repeating its keyword list.
Checkpoint
Check: Keyword arguments and required keywords · Varargs, splatting and kwargs
Several results come back as one tuple
A function returns one value, but that value can be a tuple. spread(v) returns the mean, the standard deviation and
the count of five days of rainfall as return m, s, n, and the caller takes the tuple apart:
# Several results come back as one tuple, which the caller takes apart.
function spread(v)
n = length(v)
m = sum(v) / n
s = sqrt(sum((x - m)^2 for x in v) / (n - 1))
return m, s, n # a tuple of three values
end
rain = [12.0, 0.0, 33.5, 7.25, 18.0] # five days of rainfall in mm
r = spread(rain)
println(r, " :: ", typeof(r))
m, s, n = spread(rain)
println("mean ", m, ", std ", round(s; digits = 3), ", n ", n)
m, _, n = spread(rain) # _ drops a value you do not need
lo, hi = extrema(rain)
println("driest ", lo, ", wettest ", hi)
stats(v) = (mean = sum(v) / length(v), n = length(v)) # a named tuple
(; n, mean) = stats(rain) # take fields by name, in any order
println("n = ", n, ", mean = ", mean) from session import files, run
# A function that returns a tuple, and four ways to take the result apart.
files("returns.jl")
run("""
julia returns.jl
""") Output
$ julia returns.jl
(14.15, 12.66589909955073, 5) :: Tuple{Float64, Float64, Int64}
mean 14.15, std 12.666, n 5
driest 0.0, wettest 33.5
n = 5, mean = 14.15
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 returns_session.py
The whole result printed as (14.15, 12.66589909955073, 5) of type Tuple{Float64, Float64, Int64}. Taking it apart
into names is destructuring: Taking a tuple or another collection apart into several names in one assignment, as m, s, n = spread(v), or taking fields of a named tuple by name with (; n, mean) = stats(v)., and the run used four forms of it:
m, s, n = spread(rain)binds the three values by position.m, _, n = spread(rain)drops the middle one: the manual says a name made only of underscores can be assigned but never read.lo, hi = extrema(rain)works on any function that returns a tuple; it gave 0.0 and 33.5.(; n, mean) = stats(rain)takes fields of a named tuple by their names, in any order, so the caller cannot mix up two numbers of the same type.
Return a named tuple, (mean = …, n = …), when the results are more than two or easy to confuse; a plain tuple is
enough for a pair such as extrema.
A function may change your array, and its name should say so
Julia passes arguments by sharing: the manual says values are not copied, so a function that writes into an array it
was given changes the caller’s array. The style guide’s rule is that such a function’s name ends in !, as sort!
and push! do, and that it usually returns the changed object. The convention is often paired with a version that
copies first and leaves its argument alone:
# Two versions of one operation: normalize! changes the vector it is given, normalize leaves it alone.
function normalize!(v)
lo, hi = extrema(v)
for i in eachindex(v)
v[i] = (v[i] - lo) / (hi - lo)
end
return v
end
normalize(v) = normalize!(float.(v)) # a new Float64 vector, then the same code
raw = [10.0, 15.0, 30.0]
scaled = normalize(raw)
println("normalize: ", scaled, ", raw is still ", raw)
same = normalize!(raw)
println("normalize!: ", raw, ", and the result is raw itself: ", same === raw)
counts = [0, 5, 10]
println("normalize(counts) = ", normalize(counts))
normalize!(counts) # an Int vector cannot hold 0.5 from session import files, run
# The copying and the in-place versions of one operation, then the in-place one on a vector of integers.
files("normalize.jl")
run("""
!julia normalize.jl
""") Output
$ julia normalize.jl
normalize: [0.0, 0.25, 1.0], raw is still [10.0, 15.0, 30.0]
normalize!: [0.0, 0.25, 1.0], and the result is raw itself: true
normalize(counts) = [0.0, 0.5, 1.0]
ERROR: LoadError: InexactError: Int64(0.5)
Stacktrace:
[1] Int64
@ ./float.jl:923 [inlined]
[2] convert
@ ./number.jl:7 [inlined]
[3] setindex!
@ ./array.jl:1019 [inlined]
[4] normalize!(v::Vector{Int64})
@ Main ~/demo/normalize.jl:5
[5] top-level scope
@ ~/demo/normalize.jl:19
[6] include(mod::Module, _path::String)
@ Base ./Base.jl:309
[7] exec_options(opts::Base.JLOptions)
@ Base ./client.jl:344
[8] _start()
@ Base ./client.jl:577
in expression starting at ~/demo/normalize.jl:19
[exit status 1]
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 normalize_session.py
normalize(raw) returned [0.0, 0.25, 1.0] and left raw as it was; normalize!(raw) turned raw itself into
[0.0, 0.25, 1.0] and returned it, same === raw. The copying version is one line, normalize!(float.(v)), so the
arithmetic exists once. Step through what the two versions did with the integer vector counts:
The drawing shows the whole scene; its 7 steps are listed below.
Every step as text
- The copying call. normalize(counts) is called with the integer readings [0, 5, 10].
- A new vector. float.(counts) makes a new Vector{Float64}, [0.0, 5.0, 10.0]. counts itself is not touched.
- The shared code. normalize! runs on the copy and writes 0.0, 0.5 and 1.0 into it: the arithmetic is written once, in the in-place version.
- The result. The copy is returned. The run printed [0.0, 0.5, 1.0], and counts kept its values.
- The in-place call. normalize!(counts) writes into the vector it was given, and that vector holds Int64 values.
- The first value. The first result, (0 − 0) / (10 − 0) = 0.0, converts to the integer 0 without loss, so it is stored.
- The second value. The second result is 0.5, and no Int64 has that value. An in-place function can store only values of the element type its argument already has.
float.(v) is the copy, and it also changes the element type, so normalize(counts) worked on integers. The
in-place call could not: a Vector{Int64} cannot hold 0.5, and the run stopped with InexactError: Int64(0.5).
An in-place function on the wrong element type
Wrong: calling normalize! on a vector of integer counts, or copy(v) as the copy in normalize, which keeps the
integers. Right: copy with float.(v), as here, or write the in-place version for vectors of floats only, and let
its ! warn callers that their array changes.
Key idea
A ! at the end of a name promises that the function changes an argument; pair it with a copying version that calls
it, so the code is written once and callers choose.
Checkpoint
Check: Returning and destructuring tuples · The ! convention and in-place functions
Practise: keyword arguments and in-place functions
Add a keyword with a default
Copy deflection.jl and cantilever.jl, add a keyword units = :m to deflection that returns millimetres when it
is :mm, add a call with units = :mm to cantilever.jl and run julia cantilever.jl. That call should print
about 0.79783, the 0.8 mm of the run before rounding, and every call that leaves units out should print what it
printed before.
These questions draw on the runs above, starting with the idea your answers show you know least; every choice explains itself as soon as you pick it:
Practice that adapts to you
Practise this lesson
18 questions about 6 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Warm-up (fresher to 2–5 years): how does an optional positional argument differ from a keyword argument in Julia?
An optional positional argument has a default but is still matched by its position: area(w, h = w) can be called
as area(3) or area(3, 4), and Julia writes one method for each way of calling it, two here, as methods(area)
listed. A keyword argument comes after a semicolon in the definition and is matched by name: deflection(F, L; E = 200e9, I) takes I = 8.356e-5 in any position among the keywords, and a keyword without a default, such as I,
must be given, or the call throws UndefKeywordError. Positional arguments suit the few inputs every call needs, in
an order everyone expects; keywords suit settings that a reader should see named at the call. Keywords also play no
part in choosing which method runs.
Two harder questions follow, about loosening an over-narrow signature and about a function whose positional arguments callers keep swapping. Decide your answer first, then open each first step:
Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.
-
Fresher to senior
A colleague's scale!(v::Vector{Float64}, k::Float64) works, but it fails for Float32 data, for an integer factor and for a view of a matrix column. How do you loosen the signature, and how do you show that the change costs no speed?
-
Mid to senior
A simulation function takes eight positional parameters, and two call sites passed the time step and the tolerance in each other's places. How would you redesign its signature, keep the old calls working for one release, and stop the mix-up from coming back?
Key takeaways
- A function has a long form, a short form and an anonymous form, and a do-block passes a multi-line anonymous
function as the first argument of a call, as
open(path, "w") do io … enddoes. - A default value for a positional argument makes one method for each way of calling, two for
area(w, h = w), and a type annotation chooses methods without making the code faster, sox::Float64only shuts out other types such asFloat32. - Keyword arguments follow a semicolon and are matched by name; a keyword without a default is required, and leaving it
out throws
UndefKeywordError. xs...gathers any number of arguments into a tuple,v...in a call spreads a collection into arguments, andkwargs...passes keywords on to another function.- Several results come back as a tuple or a named tuple, taken apart by position, with
_for a value you skip, or by field name with(; n, mean) = …. - A function whose name ends in
!changes an argument, which the caller shares; a copying version such asnormalize(v) = normalize!(float.(v))keeps the arithmetic in one place.
Terms in this lesson
- destructuring
- Taking a tuple or another collection apart into several names in one assignment, as m, s, n = spread(v), or taking fields of a named tuple by name with (; n, mean) = stats(v).
- do-block
- Syntax that writes a multi-line anonymous function after a call and passes it as the call's first argument: map(v) do x … end is map(x -> …, v).
- keyword argument
- An argument declared after a semicolon and passed by name, as I in deflection(F, L; E = 200e9, I). A keyword without a default must be given, or the call throws UndefKeywordError.
- optional argument
- A positional argument with a default value, as h in area(w, h = w). It is still matched by position, and Julia writes one method for each number of arguments the function accepts.
- splatting
- Writing ... after a collection in a call, as total(v...), so that its elements are passed as separate arguments instead of one.
- varargs
- A function that accepts any number of arguments, written with ... after the last parameter, as total(xs...). Inside the function, xs is a tuple of the arguments passed, empty when there are none.
References
- Functions: argument passing, return values, optional and keyword arguments, varargs, do-blocks and composition (The Julia Project)
- Methods: optional and keyword arguments and dispatch (The Julia Project)
- Documentation: writing docstrings and reading them in the REPL (The Julia Project)
- Style guide: append ! to names of functions that modify their arguments, and avoid overly specific types (The Julia Project)
- Performance tips: specialization on argument types (The Julia Project)
- Essentials: the function, do and return keywords (The Julia Project)
- Variables: names made only of underscores (The Julia Project)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress