RFC: Language Support for Traits — Yay or Nay?

What I think you’re looking for is the sort-of dual to Union - instead of being a subtype of one of its parameters, you’d want to require T to be a subtype of ALL of its parameters (I don’t recall the proper type theoretic name, so I’ll just call this Join for now - think of it as a non-eager typejoin, just aggregating types without collapsing to Any or some Union). That is, in your Platypus example you’d write it like

abstract type HasDuckBill end
abstract type LaysEggs end

abstract type Mammal end

struct Platypus <: Join{HasDuckBill, LaysEggs, Mammal} end
struct CanadianGoose <: Join{HasDuckBill, LaysEggs, Bird} end

foo(arr::AbstractArray{T}) where {T <: Join{HasDuckBill, LaysEggs}} = "Platypus or Goose"

This can of course lead to lots of ambiguity errors, e.g. if we just add the innocent looking

foo(arr::AbstractArray{T}) where {T <: Mammal} = "Not a bird!"

then

foo(Platypus[])

is ambiguous. This particular approach still has the disadvantage of not being able to easily add new traits to existing objects, since it keeps the type system of julia nominal (see the docs and wikipedia). This is in principle fixable - just don’t require Join to be declared a supertype, i.e. don’t make it a nominal part of the type system - but I haven’t thought about the consequences of that too deeply. It’s so far the best I’ve come up with to introduce traits & multiple abstract subtyping to julia though :person_shrugging: I quite like it, but solving the issues with it I haven’t found yet and actually implementing that is probably worth a master level thesis (who knows, maybe I’ll come back to this approach when I go for a masters…)

4 Likes