Return Type Annotations
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
Applies to any and all return statements
How?
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
function add{T<:Number,S<:Number}(x::Nullable{T}, y::Nullable{S})::Nullable{promote_type(T, S)}
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
@test add(2, 2) == 16
Maybe not that creative...
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
Using Return Type Annotations Effectively
By Eric Davies
Using Return Type Annotations Effectively
- 1,725