# The (missing) harm of manual dispatch in Julia

September 24, 2020

Just a short one today: New users of Julia coming from other dynamically typed languages might write functions like

function foo(x)
if x isa Int
x + x
elseif x isa Float64
x / 2.0
elseif x isa String
length(x)
else
1
end
end


This is clearly unidiomatic and should be written as

bar(x::Int) = x + x
bar(x::Float64) = x / 2.0
bar(x::String) = length(x)
bar(x) = 1 # or bar(x::Any), to be explicit


because it is nicer to read and very easy to extend for other types of x. Well, you technically can extend foo the same way as bar, but reading the definition of foo one would expect to then know its complete behavior, so it would be misleading.

However, my point is that there is actually (maybe surprisingly) no harm at runtime for code as in foo! The Julia compiler isn’t tricked that easily and will still produce optimal machine code for each type of the argument:

julia> @code_native debuginfo=:none bar(1)
.text
leaq	(%rdi,%rdi), %rax
retq
nopw	%cs:(%rax,%rax)

julia> @code_native debuginfo=:none foo(1)
.text
leaq	(%rdi,%rdi), %rax
retq
nopw	%cs:(%rax,%rax)


Both functions basically compile down to a single adding instruction (leaq; retq is for returning from the function and nopw is an operation that does nothing and is there for technical reasons). Think about it this way: When Julia compiles foo(1), it knows that 1 is of type Int, can evaluate the x isa Int expression at compile time to true, and discard everything but the x + x without a problem.

## “Appendix”

For completeness, here is what’s produced in the other cases:

julia> @code_native debuginfo=:none bar(1.0)
.text
movabsq	$140581530607976, %rax # imm = 0x7FDBB031A168 vmulsd (%rax), %xmm0, %xmm0 retq nop julia> @code_native debuginfo=:none foo(1.0) .text movabsq$140581530608088, %rax  # imm = 0x7FDBB031A1D8
vmulsd	(%rax), %xmm0, %xmm0
retq
nop

julia> @code_native debuginfo=:none bar("abc")
.text
pushq	%rax
movabsq	$"ncodeunits;", %rax callq *%rax popq %rcx retq nop julia> @code_native debuginfo=:none foo("abc") .text pushq %rax movabsq$"ncodeunits;", %rax
callq	*%rax
popq	%rcx
retq
nop

julia> @code_native debuginfo=:none bar([])
.text
movl	$1, %eax retq nopw %cs:(%rax,%rax) julia> @code_native debuginfo=:none foo([]) .text movl$1, %eax
retq
nopw	%cs:(%rax,%rax)