Traits.jl icon indicating copy to clipboard operation
Traits.jl copied to clipboard

A corner case that trait call does not seem to fall back to base function

Open eisthf opened this issue 5 years ago • 1 comments

I tried the following code and the result is not what I expect.

using Traits

IsGoodTrait(x) = IsGoodTrait(typeof(x))
IsGoodTrait(::Type) = false
IsGoodTrait(::Type{<:AbstractFloat}) = true
IsGoodTrait(::Type{<:AbstractString}) = true

# base function
foo(x) = "not implemented"

@traits function foo(x::T) where {T<:Number, IsGoodTrait(T)}
    println("$T has Good trait")
end

I get the following result. Maybe this is a bug?

julia> foo("s")
"not implemented"

julia> foo(1.2)
Float64 has Good trait

julia> foo(1) # I think this should return "not implemented"
ERROR: MethodError: no method matching '__traits__.Main.foo'(::Type{Tuple{Traits.Syntax._BetweenCurliesAndArgs,T} where T<:Number}, ::Int64, ::Traits.Syntax._BetweenArgsAndTypeVars, ::Type{Int64}, ::Traits.Syntax._BetweenTypeVarsAndTraits, ::Val{false})
Closest candidates are:
  '__traits__.Main.foo'(::Type{Tuple{Traits.Syntax._BetweenCurliesAndArgs,T} where T<:Number}, ::Any, ::Traits.Syntax._BetweenArgsAndTypeVars, ::Any, ::Traits.Syntax._BetweenTypeVarsAndTraits, ::Val{true}) at e:\work\julia\test\test.jl:11
  '__traits__.Main.foo'(::Type{Tuple{Traits.Syntax._BetweenCurliesAndArgs,T} where T<:Number}, ::Type{Traits.Syntax.InnerFuncFixedDocSig{Tuple{Pair{:a1,:x}},Tuple{Pair{:T1,:T}},Tuple{Pair{Symbol("Val{IsGoodTrait(T1)}()"),Symbol("var\"'Val{IsGoodTrait(T1)}()'\"::Val{true}")}}}}) at E:\.julia\packages\Traits\EKIlj\src\Syntax\Syntax.jl:464
Stacktrace:
 [1] #foo#9(::Base.Iterators.Pairs{Union{},Union{},Tuple{},NamedTuple{(),Tuple{}}}, ::typeof(foo), ::Int64) at e:\work\julia\test\test.jl:10
 [2] foo(::Int64) at e:\work\julia\test\test.jl:10
 [3] top-level scope at REPL[3]:1

eisthf avatar Mar 17 '20 07:03 eisthf

Thanks for reporting. Just checked your example. First the solution, then the explanation.

You have to add the following clause for it to work

@traits function foo(x::T) where {T<:Number}
    "not implemented"
end

Now the reason: @traits dispatch currently works like it constructs an outer function for normal dispatch (here the T<:Number belongs to normal dispatch). And within this outer function a call to an inner function is made which dispatches on respective extra traits.

Hence if you call foo(1), the outer function for your traits dispatch is called, however the respective inner function is only defined for IsGoodTrait(T) == true. Hence you get the no method found error. With the added fallback, the respective innerfunction has a proper fallback.

But you are right, I myself see that one would like to have a generic fallback possibility. Lets discuss more about if and how something like that would be possible in Future.

Last remark: You can inspect the current definition of a traits definition via @traits_show_implementation foo and you will see the outer and inner function respectively

schlichtanders avatar Mar 17 '20 12:03 schlichtanders