# What?

## Type Assertion

`x = 4::Int`

"this value must be an Int"

## Type Annotation

```x::Int = 4
```

"this variable must contain an Int"

## Return Type Annotation

```function x_func()::Int
return 4
end
```

"this method must return an Int"

## Can depend on types

``````# base/complex.jl
function ^(z::Complex{T}, p::Complex{T})::Complex{T} where T<:AbstractFloat
if p == 2 #square
zr, zi = reim(z)
x = (zr-zi)*(zr+zi)
y = 2*zr*zi
if isnan(x)
if isinf(y)
x = copysign(zero(T),zr)
elseif isinf(zi)
x = convert(T,-Inf)
elseif isinf(zr)
x = convert(T,Inf)
end
elseif isnan(y) && isinf(x)
y = copysign(zero(T), y)
end
Complex(x,y)
elseif z!=0
if p!=0 && isinteger(p)
rp = real(p)
if rp < 0
return power_by_squaring(inv(z), convert(Integer, -rp))
else
return power_by_squaring(z, convert(Integer, rp))
end
end
exp(p*log(z))
elseif p!=0 #0^p
zero(z) #CHECK SIGNS
else #0^0
zer = copysign(zero(T),real(p))*copysign(zero(T),imag(z))
Complex(one(T), zer)
end
end``````

## Can depend on functions of types

``````# base/bool.jl
function *(x::Bool, y::T)::promote_type(Bool,T) where T<:Unsigned
return ifelse(x, y, zero(y))
end``````

## Can depend on functions of arguments

``````# base/random.jl
function nth(iter, n::Integer)::eltype(iter)
for (i, x) in enumerate(iter)
i == n && return x
end
end``````

# Magic!

## aka lowering

``````julia> foo()::Int = 3
foo (generic function with 1 method)

julia> @code_typed foo()
CodeInfo(:(begin
return 3
end))=>Int64

julia> @code_lowered foo()
CodeInfo(:(begin
nothing
return (Core.typeassert)((Base.convert)(Main.Int, 3), Main.Int)
end))``````

becomes

`foo()::Int = 3`
`foo() = convert(Int, 3)::Int`

# Why?

## Why?

• Compiler hints
``````# src/JuMP.jl
# Returns the number of rows used by SDP constraints in the
# MPB conic representation (excluding symmetry constraints)
#   Julia seems to not be able to infer the return type
#   (probably because c.terms is Any) so getNumSDPRows tries
#   to call zero(Any)... Using ::Int solves this issue
function getNumRows(c::SDConstraint)::Int
n = size(c.terms, 1)
(n * (n+1)) ÷ 2
end``````

## Why?

• Compiler hints
• Simplifying output conversions
``````# base/hashing2.jl
function decompose(x::BigFloat)::Tuple{BigInt, Int, Int}
isnan(x) && return 0, 0, 0
isinf(x) && return x.sign, 0, 0
x == 0 && return 0, 0, x.sign
s = BigInt()
s.size = cld(x.prec, 8*sizeof(GMP.Limb)) # limbs
b = s.size * sizeof(GMP.Limb)            # bytes
ccall((:__gmpz_realloc2, :libgmp), Void, (Ptr{BigInt}, Culong), &s, 8b) # bits
ccall(:memcpy, Ptr{Void}, (Ptr{Void}, Ptr{Void}, Csize_t), s.d, x.d, b) # bytes
s, x.exp - 8b, x.sign
end``````
``````# this slide
if !isnull(x) && !isnull(y)
get(x) + get(y)
else
Nullable()
end
end``````

## Why?

• Compiler hints
• Simplifying output conversions
• Documentation!
``````"""
Filter

I wrapper around a function that takes a log `Record` and returns
a bool whether to skip logging it.

# Fields
`f::Function`: a function that should return a bool given a `Record`
"""
immutable Filter
f::Function
end

function (filter::Filter)(rec::Record)::Bool
return filter.f(rec)
end``````

# Let's get creative!

``````using Base.Test

# trick the compiler into allowing us to construct a type which
# dispatches as Number but has a distinct name
const Squared = Union{<:Number, <:Number}

Base.convert(::Type{Squared}, x::Number) = x * x

add(x, y)::Squared = x + y

# ResultTypes.jl

## By Eric Davies

### That's me!

`Result{T, E<:Exception}`
• value-or-error type for Julia
• big performance gains by avoiding try-catch
• uses return type annotations to look nice!
``````const DivResult = Result{Int, DivideError}

function integer_division(x::Int, y::Int)::DivResult
if y == 0
return DivideError()
else
return div(x, y)
end
end``````
``````function func1(x,y)
local z
try
z = div(x,y)
catch e
z = 0
end

return z
end``````
``````function func2(x, y)
r = integer_division(x,y)
if iserror(r)
return 0
else
return unwrap(r)
end
end``````
``````function func1(x,y)
local z
try
z = div(x,y)
catch e
z = 0
end

return z
end``````
``````julia> t2 = @benchmark for i = 1:10
func2(3, i % 2)
end
BenchmarkTools.Trial:
memory estimate:  0 bytes
allocs estimate:  0
--------------
minimum time:     98.952 ns (0.00% GC)
median time:      99.782 ns (0.00% GC)
mean time:        106.640 ns (0.00% GC)
maximum time:     1.519 μs (0.00% GC)
--------------
samples:          10000
evals/sample:     925``````
``````function func2(x, y)
r = integer_division(x,y)
if iserror(r)
return 0
else
return unwrap(r)
end
end``````
``````julia> t1 = @benchmark for i = 1:10
func1(3, i % 2)
end
BenchmarkTools.Trial:
memory estimate:  0 bytes
allocs estimate:  0
--------------
minimum time:     321.802 μs (0.00% GC)
median time:      330.396 μs (0.00% GC)
mean time:        376.953 μs (0.00% GC)
maximum time:     7.356 ms (0.00% GC)
--------------
samples:          10000
evals/sample:     1``````

## By Using DivResult:

• We allocate the same amount of memory (none)
• We take 0.03% of the time!

# worth it

## But Wait!

• We took the average case
• What if we never expect an error?
• ResultTypes used to do worse, but now...
``````function func1(x,y)
local z
try
z = div(x,y)
catch e
z = 0
end

return z
end``````
``````julia> t2 = @benchmark for i = 1:2:20
func2(3, i % 2)
end
BenchmarkTools.Trial:
memory estimate:  0 bytes
allocs estimate:  0
--------------
minimum time:     180.738 ns (0.00% GC)
median time:      182.029 ns (0.00% GC)
mean time:        200.481 ns (0.00% GC)
maximum time:     1.426 μs (0.00% GC)
--------------
samples:          10000
evals/sample:     695
``````
``````function func2(x, y)
r = integer_division(x,y)
if iserror(r)
return 0
else
return unwrap(r)
end
end``````
``````julia> t1 = @benchmark for i = 1:2:20
func1(3, i % 2)
end
BenchmarkTools.Trial:
memory estimate:  0 bytes
allocs estimate:  0
--------------
minimum time:     252.695 ns (0.00% GC)
median time:      256.705 ns (0.00% GC)
mean time:        280.556 ns (0.00% GC)
maximum time:     2.049 μs (0.00% GC)
--------------
samples:          10000
evals/sample:     390``````

## By Using DivResult:

• We allocate the same amount of memory (none)
• We take 70% of the time!

# still worth it

## ResultTypes.jl caveat

We gain speed by avoiding tracebacks...

so we lose tracebacks

## Takeaways

• Use return type annotations for:
• Compiler hints
• Simplifying output conversions
• Documentation
• Also maybe try ResultTypes.jl

Eric Davies @iamed2

By Eric Davies

• 1,648