Defining pow :^ does not have a fallback for irrationals (method ambiguous)


#1
julia> struct Test <: AbstractFloat
       x
       end
julia> Base.:^(x::Number, y::Test) =  x^(y.x)


# does not work
julia> e^Test(2.0)
ERROR: MethodError: ^(::Irrational{:e}, ::Test) is ambiguous. Candidates:
  ^(x::Number, y::Test) in Main at REPL[3]:1
  ^(::Irrational{:e}, x::Number) in Base at irrationals.jl:219
Possible fix, define
  ^(::Irrational{:e}, ::Test)

That should just work since Irrational <: Number; I would think that there should be an appropriate float conversion fallback.


#2
struct Test{T <: AbstractFloat}
    x::T
end

Base.:^(x::T, y::Test) where {T<:Number} =  x^(y.x)
julia> e^Test(2.0)
7.38905609893065

#4

Actually the real issue here is that what you suggest doesn’t work if you add

struct Test{T <: AbstractFloat} <: AbstractFloat
    x::T
end
Base.:^(x::T, y::Test) where {T<:Number} =  x^(y.x)

julia> e^Test(2.0)
ERROR: MethodError: ^(::Irrational{:e}, ::Test{Float64}) is ambiguous. Candidates:
  ^(x::Number, y::Test) in Main at REPL[1]:1
  ^(::Irrational{:e}, x::Number) in Base at irrationals.jl:219
Possible fix, define
  ^(::Irrational{:e}, ::Test)

In other words I need that Test <: AbstractFloat


#5

Looks like a peculiarity of e, because it falls back on exp:

julia> struct Test{T<:AbstractFloat} <: AbstractFloat
       x::T
       end

julia> Base.:^(x::T, y::S) where {T<:Number, S<:Test} = x ^ (y.x)

julia> pi ^ Test(2.0)
9.869604401089358

julia> im ^ Test(2.0)
-1.0 + 0.0im

julia> 5.7 ^ Test(2.0)
32.49

julia> e ^ Test(2.0)
ERROR: not implemented for Test{Float64}
Stacktrace:
 [1] ^(::Irrational{:e}, ::Test{Float64}) at ./irrationals.jl:223

julia> exp(Test(2.0))
ERROR: not implemented for Test{Float64}
Stacktrace:
 [1] exp(::Test{Float64}) at ./math.jl:275

julia> Base.:^(x::Irrational{:e}, y::S) where {S<:Test} = e ^ (y.x)

julia> e ^ Test(2.0)
7.38905609893065

#6

It does seem the error message is meaningful.
There does indeed seem to be an ambiguity.

Which method is more specific?
^(x::Number, y::Test)
^(::Irrational{:e}, x::Number)

Have you tried following the suggested fix?

Possible fix, define
  ^(::Irrational{:e}, ::Test)
struct Test{T<:AbstractFloat} <: AbstractFloat
    x::T
end

Base.:^(x::T, y::Test) where {T<:Number} =  x^(y.x)
Base.:^(::Irrational{:e}, y::Test) = exp(y.x)
Julia-0.6.0> e^Test(2.0)
7.38905609893065

#7

Right, I was trying to better understand whether the current fallbacks make the most sense.

I feel like Base.exp(x::AbstractFloat) = float(e)^x would make sense to define in base e.g.:

struct Test{T<:AbstractFloat} <: AbstractFloat
    x::T
end
Base.convert(::Type{T}, x::Test{T}) where T<:AbstractFloat = x.x
Base.promote_rule(::Type{T},::Type{Test{T}}) where {T<:AbstractFloat} = T
Base.:^(x::T, y::Test{T}) where {T<:AbstractFloat} =  x^(y.x)

x = Test(2.0)

julia> γ^x  # works!
0.33317792380771866

julia> e^x # nope
ERROR: not implemented for Test{Float64}
Stacktrace:
 [1] ^(::Irrational{:e}, ::Test{Float64}) at .\irrationals.jl:219

Perhaps base julia needs to define

Base.exp(x::AbstractFloat) = float(e)^x
julia> e^x # now works 
7.3890560989306495

#8

I don’t think this is a good idea:

julia> struct Test{T<:AbstractFloat} <: AbstractFloat
       x::T
       end

julia> Base.exp(x::Test) = float(e) ^ (x.x)

julia> e ^ Test(big"2.0")
7.389056098930649441295292229390736727411671930796332859195060284440703668451533

julia> exp(big"2.0")
7.389056098930650227230427460575007813180315570551847324087127822522573796079054