A system for dispatching on conjunctions of traits.
Basic idea:
- Type hierarchies are oppresive — we'd rather simply specify that values and types have particular properties. For instance that the type is indexable. Sometimes these kinds of properties are called traits.
- Often we want to write methods that are specific to particular combinations of traits. That is, we want to say:
- do something if type has trait A
- do something else if type has trait A and B
- do something else if type has trait A and B and C
There are many implementations of traits in Julia which do 1 but as far as I am aware this is the first approach that supports conjunctions of traits.
First make some traits
# This is a trait
const Iterable = Trait{:Iterable}
# This is another trait
const Indexable = Trait{:Indexable}
Now let's give some types these traits
AndTraits.traits(::Vector) = Iterable ∧ Indexable
AndTraits.traits(::Set) = Iterable
Now let's define some methods that use these trarts
g(x) = g(traits(x), x)
g(::traitmatch(Iterable), x) = "This is a string"
g(::traitmatch(Iterable, Indexable), x) = 21
Now let's evaluate g
on some different inputs
@test g(x) == 21
@test g(Set([1,2,3])) == "This is a string"
A type can belong to many traits
Holy traits (which have been syntactically sugared in SimpleTraits.jl) allow you to add multiple traits to a type. But, when you use the traits, you have to decide at the point of dispatch which trait you care about.
So for example we can define the trait SomeTrait
as above, but we also need to define a trait dispatch method hasSomeTrait
, such that hasSomeTrait(t)
So you right methods of the form:
f(x) = f(hasSomeTrait(x), x)
f(::SomeTrait, x) = ...
This is fine, but it inhibits extensibility.