Julia does dispatching on default values. So what you can do is:
function f(x,y=nothing;z=:SomeOption)
if y == nothing
return 5
else # y is some number
if z == :SomeOption
return x+2y
else
return x+3y
end
end
end
Via dispatch, it will compile a different function when y is a number, so you don’t need to worry about the internal checks. To see this, notice that when y is not given, all of the other logic compiles away:
julia> @code_llvm f(2)
; Function Attrs: uwtable
define i64 @julia_f_68264(i64) #0 !dbg !5 {
top:
ret i64 5
}
and thisis just a function which returns 5. In the other case:
julia> @code_llvm f(2,3)
; Function Attrs: uwtable
define i64 @julia_f_68300(i64, i64) #0 !dbg !5 {
top:
%2 = call i8**** @jl_get_ptls_states() #4
%3 = alloca [7 x i8**], align 8
%.sub = getelementptr inbounds [7 x i8**], [7 x i8**]* %3, i64 0, i64 0
%4 = getelementptr [7 x i8**], [7 x i8**]* %3, i64 0, i64 2
%5 = bitcast i8*** %4 to i8*
call void @llvm.memset.p0i8.i32(i8* %5, i8 0, i32 40, i32 8, i1 false)
%6 = bitcast [7 x i8**]* %3 to i64*
store i64 10, i64* %6, align 8
%7 = getelementptr [7 x i8**], [7 x i8**]* %3, i64 0, i64 1
%8 = bitcast i8**** %2 to i64*
%9 = load i64, i64* %8, align 8
%10 = bitcast i8*** %7 to i64*
store i64 %9, i64* %10, align 8
store i8*** %.sub, i8**** %2, align 8
%11 = getelementptr [7 x i8**], [7 x i8**]* %3, i64 0, i64 6
%12 = getelementptr [7 x i8**], [7 x i8**]* %3, i64 0, i64 5
%13 = getelementptr [7 x i8**], [7 x i8**]* %3, i64 0, i64 4
%14 = getelementptr [7 x i8**], [7 x i8**]* %3, i64 0, i64 3
store i8** inttoptr (i64 2147434592 to i8**), i8*** %4, align 8
store i8** inttoptr (i64 71900760 to i8**), i8*** %14, align 8
store i8** inttoptr (i64 2147434584 to i8**), i8*** %13, align 8
%15 = call i8** @jl_box_int64(i64 signext %0)
store i8** %15, i8*** %12, align 8
%16 = call i8** @jl_box_int64(i64 signext %1)
store i8** %16, i8*** %11, align 8
%17 = call i8** @jl_invoke(i8** inttoptr (i64 2271350544 to i8**), i8*** %4, i32 5)
%18 = bitcast i8** %17 to i64*
%19 = load i64, i64* %18, align 16
%20 = load i64, i64* %10, align 8
store i64 %20, i64* %8, align 8
ret i64 %19
}
it compiles to a function which has to do a check on symbols and branch etc.
A better way, sometimes, is to use dispatch:
f(x;z=:SomeOption) = 5
function f(x,y;z=:SomeOption)
if z == :SomeOption
return x+2y
else
return x+3y
end
end
this is equivalent to the code above but may organize your code better in some cases. You can think of this as the “no default values” case.
A very common way to use this is, if you have a lot of code that you want to share, have an internal function named _f that does the big calculations. Then you have your different dispatches do setup and call _f. This organizes by dispatch but still re-uses most of the internal code.