Mandatory vs. optional arguments to functions

Code:

#!/usr/bin/env julia

f(x=0)  = println("f(x=0): x ", x)
f()
f(1)

println("methods(f)")
println(methods(f))
println()

f(x)    = println("f(x)  : x ", x)
f()
f(2)

println("methods(f)")
println(methods(f))

Output:

2024_05_28 17:13:46 ~/temp $ function_call_with_multiple_parens.jl 
f(x=0): x 0
f(x=0): x 1
methods(f)
# 2 methods for generic function "f" from Main:
 [1] f()
     @ ~/temp/function_call_with_multiple_parens.jl:3
 [2] f(x)
     @ ~/temp/function_call_with_multiple_parens.jl:3

f(x)  : x 0
f(x)  : x 2
methods(f)
# 2 methods for generic function "f" from Main:
 [1] f()
     @ ~/temp/function_call_with_multiple_parens.jl:3
 [2] f(x)
     @ ~/temp/function_call_with_multiple_parens.jl:11

2024_05_28 17:13:48 ~/temp $ 

The code defines a function f with an optional argument on line 3 and calls it on lines 4 and 5. So far, so good. The methods call shows the line numbers of the implementation. Apparently this optional argument calls two methods into existence, one not taking any arguments and one that takes one argument.

On line 11 the definition of f(x) is overwritten as the methods calls indicates. So now I had expected that f() of line 12 would call the definition of line 3 as suggested by methods. Instead, it takes the default value 0 from line 3 and uses it to execute the function body of line 11. The call f(2) works as expected.

I had expected that the function calls on line 4 and 12, both f(), would use the body of line 3. I had not expected that the default of line 3 would be used with the body of line 11. What is the explanation for this?

In Julia what looks like a function definition is always a method definition. Adding default values of arguments is logically equivalent to multiple method definitions. That is why your first definition of f(x=0) creates two methods.

Your later definition of f(x) overrides the second method but the first method, f() = f(0), still exists.

6 Likes

One way to put it: f(x=0) = println("f(x=0): ", x) does not create f(x) = println("f(x=0): ", x) and f() = println ("f(x=0): ", 0) but f() = f(0), so overwriting f(::Any) changes the behavior of both methods.