From time to time I’ve been thinking about how traits might work in the future in Julia. At JuliaCon 2016 there was some discussion of syntax involving an extra set of ::
, such as f(a::AbstractArray::Dense)
instead of f(a::DenseArray)
. We played with this syntax in Traitor.jl but it was more of a toy model or demonstration - nonetheless I did implement (I think) a feasible multiple-argument, multiple-trait dispatch algorithm in a disgusting generated function.
I’d like to suggest an alternative formulation using the new where
syntax, and ask if this is even theoretically feasible in the framework of UnionAll
.
@Jeff.Bezanson has a branch (jb/subtype) which allows one to specify UnionAll
types using syntax like Array{T} where T <: Real
. The thing on the right of appears to be a predicate; an indicator function of which types of T
are allowed, if you will.
Now we could imagine allowing arbitrary predicates on the RHS but while one can easily check if the predicate is true for a given concrete type, dispatch rules also require that we can calculate whether set of types (or UnionAll
) is a superset of another (or ambiguous). So having Array{T,N} where f(N)
for instance would be difficult to dispatch on since we cannot rank specificity compared to, e.g., Array{T,N} where g(N)
for arbitrary f
and g
.
However, what if the predicate could be a function (or trait) which returns a type? So we could have
getindex(a::A, inds...) where A <: AbstractArray where linearindexing(A) <: LinearFast = ...
The method table for getindex
would have to know about the trait linearindexing
and compare (using the standard type tree) the specificity of this trait of all matching methods. If the trait doesn’t exist in another method definition, then Any
is taken for that trait. A method would be selected from the possibly matching methods if and only if all the traits (in this picture I consider the type as simply one of the traits) are at least as specific as the others. Just like standard methods, there is the possibility for ambiguities.
This seemed to work quite well in Traitor.jl - I could have methods with arbitrary numbers of arguments and arbitrary numbers of traits each, and they would dispatch (or be ambiguous) as I predicted they should. (Note that having an unknown and arbitrary number of traits is currently much more difficult in Julia than the single-trait “Tim Holy Trait Trick”-type of thunk).
The question here is whether it would be feasible to add these here to the UnionAll
types. We can make an trait-imbued UnionAll
type with syntax like
A where A <: AbstractArray where linearindexing(A) <: LinearFast
A UnionAll
could contain a list of trait keys and their associated upper bounds, one trait being the actual type. As far as I can tell, it would still be feasible to determine if B <: A by comparing all the traits that appear in A
or B
. Does this seem correct? Is there a counterexample?