print and println@printftry-catch for Exception HandlingfinallyFunctions become truly versatile when they can operate on different data. You provide this data through arguments, which are the values passed to a function when it is called. Inside the function, these arguments are received by parameters, which are special variables defined in the function's signature. Think of parameters as named placeholders in your function's definition, waiting to be filled with the actual data (arguments) you supply during a function call.
Understanding how to define and use these parameters effectively is fundamental to writing flexible and readable Julia code. Julia offers a few ways to handle function arguments, primarily distinguishing between positional and keyword arguments.
Before we look at the different types of arguments, let's clarify the terms:
Consider a simple function to add two numbers:
function add_numbers(x, y) # x and y are parameters
return x + y
end
result = add_numbers(5, 3) # 5 and 3 are arguments
println(result) # Output: 8
Here, x and y are parameters of the add_numbers function. When we call add_numbers(5, 3), the value 5 is the argument passed to the parameter x, and 3 is the argument passed to y.
The most straightforward way to pass arguments to a function is by position. When you call a function with positional arguments, Julia matches the arguments to the parameters based on their order. The first argument goes to the first parameter, the second to the second, and so on.
function describe_pet(animal_type, pet_name)
println("I have a ", animal_type, " named ", pet_name, ".")
end
describe_pet("cat", "Whiskers") # "cat" maps to animal_type, "Whiskers" to pet_name
describe_pet("Spike", "dog") # "Spike" maps to animal_type, "dog" to pet_name. Oops!
In the first call to describe_pet, "cat" is assigned to animal_type and "Whiskers" to pet_name. The output is:
I have a cat named Whiskers.
However, in the second call, the order is inadvertently swapped. "Spike" is assigned to animal_type and "dog" to pet_name, leading to:
I have a Spike named dog.
This example highlights the importance of order with positional arguments. For functions with several positional arguments, it can sometimes be difficult to remember the correct sequence. This is where keyword arguments can be very helpful.
Keyword arguments are identified by their name (the parameter name) rather than their position. This makes function calls more explicit and readable, especially when a function has many parameters or when you want to skip optional parameters that have default values (which we'll cover in the next section).
You define keyword arguments in a function signature after all positional arguments and a semicolon (;).
function create_user_profile(username; email="not_provided", active=true)
println("User: ", username)
println("Email: ", email)
println("Active: ", active)
end
# Calling with keyword arguments
create_user_profile("julia_fan"; email="[email protected]", active=false)
create_user_profile("coder123"; active=true) # email uses its default
create_user_profile("data_sci"; email="[email protected]") # active uses its default
# Order of keyword arguments doesn't matter
create_user_profile("another_user"; active=false, email="[email protected]")
In the calls above:
username is a positional argument.email and active are keyword arguments. Notice the semicolon in the function definition function create_user_profile(username; email="not_provided", active=true).parameter_name=value.active=false, email="[email protected]" works the same as email="[email protected]", active=false).email and active here), you can omit it in the function call, and the default will be used.The use of a semicolon ; in the function definition is key to separating positional arguments from keyword arguments. If a function only has keyword arguments, the semicolon is still used:
function set_preferences(; theme="dark", notifications=true)
println("Theme set to: ", theme)
println("Notifications: ", notifications ? "enabled" : "disabled")
end
set_preferences(notifications=false)
set_preferences(theme="light", notifications=true)
The following diagram illustrates how arguments map to parameters for both positional and keyword types.
Comparison of how positional and keyword arguments are matched to parameters.
Functions in Julia can accept both positional and keyword arguments. When defining such a function, all positional parameters must be listed before the semicolon, and all keyword parameters after it.
function send_message(recipient, message_text; sender="System", priority="Normal")
println("To: ", recipient)
println("From: ", sender)
println("Priority: ", priority)
println("Message: ", message_text)
end
send_message("[email protected]", "Your report is ready.")
send_message("[email protected]", "Server maintenance soon.", priority="High", sender="IT Dept")
In these calls:
recipient and message_text are positional arguments. Their values are determined by their order in the call.sender and priority are keyword arguments. They can be omitted (to use defaults) or specified by name.When calling a function that takes both types, you must provide all required positional arguments first, in order, followed by any keyword arguments (preceded by a semicolon if necessary for disambiguation, or simply listed after positional arguments if the context is clear).
# Correct call
send_message("test_user", "Test message text"; sender="QA Team")
# Incorrect: keyword argument before positional
# send_message(sender="QA Team", "test_user", "Test message text") # This would error
Sometimes, you want a function to accept a variable number of arguments. Julia handles this with the "splat" operator (...).
If you append ... to the last positional parameter in a function definition, that parameter will collect all remaining positional arguments passed to the function into a tuple.
function list_items(list_name, items...)
println(list_name, ":")
if isempty(items)
println(" (No items)")
else
for item in items
println(" - ", item)
end
end
end
list_items("Shopping List", "apples", "bananas", "carrots")
list_items("To-Do")
list_items("Meeting Attendees", "Alice", "Bob")
In the first call, list_name is "Shopping List", and items becomes the tuple ("apples", "bananas", "carrots").
In the second call, list_name is "To-Do", and items is an empty tuple ().
In the third call, list_name is "Meeting Attendees", and items is ("Alice", "Bob").
This feature is very useful for functions like println (which can take any number of arguments to print) or mathematical functions that might operate on a varying number of inputs (e.g., a function to sum any quantity of numbers).
Similarly, you can collect all otherwise unassigned keyword arguments into a NamedTuple using kwargs... after the semicolon. This is useful for functions that might pass options through to other functions or handle arbitrary keyword parameters.
function process_options(main_option; extra_options...)
println("Main Option: ", main_option)
if !isempty(extra_options)
println("Extra Options:")
for (key, value) in pairs(extra_options)
println(" ", key, " = ", value)
end
else
println("No extra options provided.")
end
end
process_options("EnableFeatureX"; log_level="debug", retries=3)
process_options("RunAnalysis")
In the first call, main_option is "EnableFeatureX". extra_options becomes a NamedTuple equivalent to (log_level="debug", retries=3).
In the second call, extra_options is an empty NamedTuple.
Using varargs adds a great deal of flexibility to your function designs.
As you learned in Chapter 2, Julia allows you to annotate variables with types. This practice extends to function parameters as well. Specifying types for parameters can make your functions easier to understand, help catch errors, and enable Julia's compiler to generate more specialized and often faster code through a mechanism called multiple dispatch (which we'll touch upon later in this chapter).
function calculate_rectangle_area(length::Real, width::Real)
if length < 0 || width < 0
println("Error: Length and width must be non-negative.")
return 0 # Or throw an error, which we'll see in Chapter 8
end
return length * width
end
area1 = calculate_rectangle_area(5.0, 3.0) # Works, Float64 is a subtype of Real
println("Area 1: ", area1)
area2 = calculate_rectangle_area(10, 2) # Works, Int is a subtype of Real
println("Area 2: ", area2)
# area3 = calculate_rectangle_area("5", "3") # This would cause a MethodError
# println("Area 3: ", area3)
In calculate_rectangle_area(length::Real, width::Real), we specify that both length and width should be subtypes of Real. This means they can be integers, floating-point numbers, etc., but not, for example, strings. If you try to call this function with arguments of incorrect types (like strings), Julia will raise a MethodError before any code inside the function runs, which helps in identifying issues early.
By understanding and utilizing positional, keyword, and variable arguments, along with type annotations, you gain fine-grained control over how your functions receive and process information, leading to more maintainable Julia programs. The next section will build upon this by showing how to provide default values for these arguments, making your functions even more flexible.
Was this section helpful?
© 2026 ApX Machine LearningEngineered with