Multiple dispatch and "x^-n equivalent to inv(x)^n for literal n"

Applying a negative integer exponent calls inv, from pull request #24240.

I have a custom type based on a string, which I would like to exponentiate, for example

struct LaTeXobj
    latex_string::String
end

x = LaTeXobj("x+3")

Base.:^( lo::LaTeXobj, p::Number ) = "($(lo.latex_string))^{$p}"
x^(-3)

The desired result would be a string "(x+3)^{-3}", but this instead calls inv. It looks like some metaprogramming is going on in literal_pow which confusingly prevents my usual approach with operators from working.

MethodError: no method matching inv(::LaTeXobj)
Closest candidates are:
  inv(!Matched::Complex{Float64}) at complex.jl:396
  inv(!Matched::BigFloat) at mpfr.jl:418
  inv(!Matched::Integer) at int.jl:56
  ...

Stacktrace:
 [1] macro expansion at ./none:0 [inlined]
 [2] literal_pow(::typeof(^), ::LaTeXobj, ::Val{-3}) at ./none:0
 [3] top-level scope at none:0

How do you implement the exponent symbol for a custom type and negative integer exponents? I could just use a symbol other than ^, but I was really hoping for that slick syntactic sugar!

One way would be to define literal_pow for literal_pow(f::typeof(^), x::LaTeXobj, ::Val{p}):

julia> struct LaTeXobj
           latex_string::String
           end

julia> x = LaTeXobj("x+3")
LaTeXobj("x+3")

julia> Base.literal_pow(f::typeof(^), x::LaTeXobj, ::Val{p}) where {p} = "($(x.latex_string))^{$p}"

julia> x^(-3)
"(x+3)^{-3}"

julia> x^3
"(x+3)^{3}"

But I agree, this feels backwards…

On a sidenote, I think you’ll take interest in LaTeXStrings.jl.

2 Likes