Julia has the power to define arbitrary dispatch helpers, like for iterators there is IteratorSize
. My new package Traits.jl gives you the power to dispatch on such helpers directly as @traits f(a) where {Base.IteratorSize(a)::Base.HasShape} = "awesome!"
.
Dispatching on helper functions is extremely useful. Iterators are a perfect example: As everything can be an iterator we need to distinguish for some extra details in order to create optimal code. For instance whether something HasLength, HasShape{N}, IsInfinite, or has SizeUnknown. Binary functions also fall into this pattern - you may want to dispatch on whether something is isempty()
.
Without Traits.jl you would use nested functions for this, like
f(a) = f_(a, Base.IteratorSize(a), Val(isempty(a)))
function f_(a, ::Base.HasShape, ::Any)
# do ... for something which HasShape
end
function f_(a, ::Base.HasShape, ::Val{false})
# do ... for something which HasShape and is not empty
end
With the new Traits.@traits
macro you can simply define them like
using Traits
@traits function f(a) where {Base.IteratorSize(a)::Base.HasShape}
# do ... for something which HasShape
end
@traits function f(a) where {Base.IteratorSize(a)::Base.HasShape, !isempty(a)}
# do ... for something which HasShape and is not empty
end
The @traits
macro abstracts away the hidden complexity of the nested function, allowing you to seamlessly extend your traits-based methods.