# Combining SimpleTraits.jl traits on one type

I recently got a little into SimpleTraits - and I like it quite well. But I have a question considering the combination of traits.

Consider the following MWE (just noticed, maybe `AbstractA` is not necessary in general)

``````using SimpleTraits
abstract type AbstractA end
abstract type DecoA <: AbstractA end
# A few concrete types
struct A <: AbstractA end
struct A1 <: DecoA end
struct A2 <: DecoA end
struct A3 <: DecoA end

# just some function
f(::AbstractA, x) = x+2
g(::DecoA, x, y) = x+y
``````

Now I want to do two decorators, for example

``````@traitdef IsNice{T}
@traitimpl IsNice{T} <- is_nice(T)
is_nice(::Type{<:A1}) = true
is_nice(::Type{<:A2}) = false
is_nice(::Type{<:DecoA}) = false

@traitdef IsCool{T}
@traitimpl IsCool{T} <- is_cool(T)
is_cool(::Type{<:A1}) = false
is_cool(T::Type{<:A2}) = true
is_cool(T::Type{<:DecoA}) = false

@traitfn f(a::TA, x) where {TA <: DecoA; IsNice{TA}} = g(a,x,3) #(a)
@traitfn f(a::TA, x) where {TA <: DecoA; IsCool{TA}} = g(a,x,5) #(b)
``````
1. what is a good way to write the case !IsNice and !IsCool ?
2. what is a good way to write the case !IsNice or !IsCool ?

More generically, if I have a set of traits is there a way to define the fallback, that none of them is active?

I want it to fallback to just using `f` i.e. I want A3 act similar to A.

``````print("normal:\$(f(A(),0))") # prints: normal:2
println("cool \$(f(A2(),0))") #prints: cool: 5
println("neither nice nor cool \$(f(A3(),0))") # does not work, since there is no !IsCool cases should behave like A (see Q1)
println("nice \$(f(A1(),0))") # does not work since it first „checks“ Cool (defined last; see Q2)
``````

This brings up the idea of preceedence - i.e.

1. If I have `A4` the is nice and cool, which one is called first, (a) or (b) and how could I steer that?
1 Like

From the README on `SimpleTraits.jl` it appears dispatching on multiple traits is not yet possible.

However `WhereTraits.jl` could possibly support it!

``````using WhereTraits

abstract type AbstractA end
abstract type DecoA <: AbstractA end

# A few concrete types
struct A <: AbstractA end
struct A1 <: DecoA end
struct A2 <: DecoA end
struct A3 <: DecoA end

# just some function
f(::AbstractA, x) = x+2
g(::DecoA, x, y) = x+y

is_nice(::Type{<:A1}) = true
is_nice(::Type{<:DecoA}) = false

is_cool(::Type{<:A2}) = true
is_cool(::Type{<:DecoA}) = false

@traits f(a::TA, x) where {TA <: DecoA, is_nice(TA)} = g(a,x,3)
@traits f(a::TA, x) where {TA <: DecoA, is_cool(TA)} = g(a,x,5)
@traits f(a::TA, x) where {TA <: DecoA, !is_cool(TA), !is_nice(TA)} = "without this the multiple dispatch is ill-defined for `A3`"

@show f(A(),0)
@show f(A1(),0)
@show f(A2(),0)
@show f(A3(),0)
``````
2 Likes

Thanks for this nice solution,
actually @mateuszbaran proposed a solution without `WhereTraits`, since that comes with a lot of dependencies and we want to use it in a lightweight interface. His solution is here

Just out of curiosity: Your approach would (in the current form) still error on both set to true (my A4) so I would have to give precedence for either cool or nice in a fourth function definition, right?

1 Like

You could just set a function with proper type signature

``````@traits f(a::TA, x) where {TA <: DecoA, is_cool(TA), is_nice(TA)} = "dispatch for `A4`"
``````

It’s a shame `WhereTraits.jl` is such a heavy weight, I would also think twice before using it as a major dependency in a package. The solution by @mateuszbaran seems like a nice way around the problem!

2 Likes

Oh, I actually exactly had such a line in mind with my question. One disadvantage of that approach (which Mateusz avoids) is that for 3 one would need 8 cases, for 4 traits 16 and so on, it does not scale well; but thanks for your input, I will report, when we managed to have a solution.

Just to finish this thread, we just released our solution to the question asked, in principle using a list of traits (ordered) here Decorating/Extending a Manifold · ManifoldsBase.jl

1 Like