Ok so I have three options:
struct A{T}
x::T
s::Bool
function A(x::Real)
xf = float(x)
return new{typeof(xf)}(xf, xf > 0)
end
end
f1(a::A) = a.s ? 2a.x : throw("Not applicable for negative parameters")
f2(a::A) = a.s ? throw("Not applicable for positive parameters") : 3a.x
f3(a::A) = a.s ? f1(a) : 1/f2(a)
struct B{T,sign}
x::T
function B(x::Real)
xf = float(x)
return new{typeof(xf), xf>0}(xf)
end
end
f1(a::B{T,true}) where T = 2a.x
f2(a::B{T,false}) where T = 3a.x
f3(a::B{T,true}) where T = f1(a)
f3(a::B{T,false}) where T = 1/f2(a)
struct PositiveC{T}
x::T
function PositiveC(x::Real)
xf = float(x)
if xf ≤ 0
throw("...")
end
return new{typeof(xf)}(xf)
end
end
struct NegativeC{T}
x::T
function NegativeC(x::Real)
xf = float(x)
if xf ≥ 0
throw("...")
end
return new{typeof(xf)}(xf)
end
end
f1(a::PositiveC) = 2a.x
f2(a::NegativeC) = 3a.x
f3(a::PositiveC) = f1(a)
f3(a::NegativeC) = 1/f2(a)
C(x) = x > 0 ? PositiveC(x) : x < 0 ? NegativeC(x) : throw("...")
main() = f3(A(-0.2)), f3(B(-0.2)), f3(C(-0.2)), f3(A(0.2)), f3(B(0.2)), f3(C(0.2))
main()
And this simply gives:
julia> @code_native main()
.text
.file "main"
.section .ltext,"axl",@progbits
.globl julia_main_12644 # -- Begin function julia_main_12644
.p2align 4, 0x90
.type julia_main_12644,@function
julia_main_12644: # @julia_main_12644
; Function Signature: main()
; ┌ @ Untitled-1:56 within `main`
.cfi_startproc
# %bb.0: # %top
push rbp
.cfi_def_cfa_offset 16
.cfi_offset rbp, -16
mov rbp, rsp
.cfi_def_cfa_register rbp
mov rax, rcx
movabs rcx, offset ".L_j_const#1"+16
; │ @ Untitled-1 within `main`
vmovups ymm0, ymmword ptr [rcx]
vmovups ymmword ptr [rax + 16], ymm0
movabs rcx, offset ".L_j_const#1"
vmovups ymm0, ymmword ptr [rcx]
vmovups ymmword ptr [rax], ymm0
pop rbp
vzeroupper
ret
.Lfunc_end0:
.size julia_main_12644, .Lfunc_end0-julia_main_12644
.cfi_endproc
; â””
# -- End function
.type ".L_j_const#1",@object # @"_j_const#1"
.section .lrodata,"al",@progbits
.p2align 3, 0x0
".L_j_const#1":
.quad 0xbffaaaaaaaaaaaaa # double -1.6666666666666665
.quad 0xbffaaaaaaaaaaaaa # double -1.6666666666666665
.quad 0xbffaaaaaaaaaaaaa # double -1.6666666666666665
.quad 0x3fd999999999999a # double 0.40000000000000002
.quad 0x3fd999999999999a # double 0.40000000000000002
.quad 0x3fd999999999999a # double 0.40000000000000002
.size ".L_j_const#1", 48
.set ".L+Core.Tuple#12646.jit", 140703350835968
.size ".L+Core.Tuple#12646.jit", 8
.section ".note.GNU-stack","",@progbits
julia>
Which means that the compiler is removing the whole nonsense we are doing and going straight to the result in all cases. So, at least in this simple case, the three are actually equivalent at runtime. I do not know how to measure compile time to see if one is better than the others…