How to call the original function when overriding it

I want to override Unitful.Base.show to make it first simplify the quantity’s unit before showing it. I don’t know how to call the original overriden function though. What I have tried:

@defonce origShow = Unitful.Base.show
@eval Unitful function Base.show(io::IO, mime::MIME"text/plain", x::Quantity)
    Main.origShow(io, mime, upreferred(x))
end
@eval Unitful function Base.show(io::IO, mime::MIME"text/plain", x::Vector{<:Quantity})
    Main.origShow(io, mime, upreferred(x))
end

This code seems to cause an infinite loop and crashes Julia.

1 Like

You can use invoke to call a more generic method of the same function:

julia> function foo(x)
         println("generic foo")
       end
foo (generic function with 1 method)

julia> function foo(x::Int)
         invoke(foo, Tuple{Any}, x)  # Call the x::Any version of `foo`
         println("specific foo")
       end
foo (generic function with 2 methods)

julia> foo(1)
generic foo
specific foo
1 Like

I want to call that exact method, not a more generic version.

invoke should be able to do that if you replace the Tuple{Any} bit with the types you are specifying.
I’m curious, though, why do you want to modify the function’s general behavior instead of using a new function like newshow(io, mime, x) = Unitful.Base.show(io, mime, upreferred(x)) or just simplifying x = upreferred(x) wherever you can before calling Unitful.Base.show?

Then invoke would call my own newly defined function, no? The type signature of the new method is identical to its super method.

I don’t define a new function because I am trying to override how some values are displayed on the REPL. I don’t want to type upreferred(...) for everything.

Oh I see what you mean now. You wanted origShow = Unitful.Base.show to “copy” the function so you can use the function name for something else. But origShow actually sticks with the name Unitful.Base.show. It works that way because when you dynamically change a function’s behavior i.e. by adding methods, you normally want methods that use that function to also change their behavior (method invalidation).

Your code just ended up being indirectly recursive, hence the suggestions to instead make another method with more specific types (to take precedence in dispatch) or a differently-named wrapper function. I think the only other straightforward way to make a different method is with a different number of arguments, like show(blah, io, mime, x) = show(io, mime, upreferred(x)). But I think you’re saying that the usual ways won’t work for this case because you’re not even calling this method directly, you’re trying to affect how the REPL decides to print something.

On 1.6 (or maybe only on master) you can use invoke_in_world for this:

julia> f(x) = print(x)
f (generic function with 1 method)

julia> w = Base.get_world_counter()
0x0000000000007418

julia> g(x) = Base.invoke_in_world(w, f, x)
g (generic function with 1 method)

julia> f(x) = begin; print("new: "); g(x); end
f (generic function with 1 method)

julia> f(2)
new: 2
6 Likes

World age always spooked me, shouldn’t be too surprised it powers such witchcraft!

Thaanks! It’s cool stuff like this that makes me love Julia.