Extending Base.show with Julia’s Multiple Dispatch
Learn how to add a custom show method for your own types using Julia’s multiple dispatch, and understand the compile‑time trade‑offs involved.
27 Aug 2026, 07:12 UTC

Problem: Printing a custom type without touching Base
When you define a new composite type in Julia, the default show output is often too verbose or not domain‑specific. You want instances of your type to print in a concise, readable form, but you prefer not to modify Julia’s Base module directly.
Thesis: Multiple dispatch lets you add a show method for your type while keeping the original behavior intact
Julia’s multiple dispatch selects a method based on the types of all arguments. By defining a new method for Base.show that specializes on your custom type, the compiler will invoke that method whenever show is called with an instance of your type, leaving all other show calls unchanged.
How multiple dispatch works (brief)
A generic function like show can have many methods. When you call show(x), Julia looks at the tuple of argument types (typeof(x), plus any keyword or I/O arguments) and picks the most specific method. The first call for a given type tuple triggers compilation; subsequent calls reuse the compiled code.
Worked example: A simple vector type
We define a mutable struct MyVec that holds two fields and then teach Julia how to display it.
# Define the type
mutable struct MyVec
a::Float64
b::Float64
end
# Import the function we want to extend (necessary in Julia 1.9+)
import Base.show
# Add a new method that specializes on MyVec and an IO stream
function show(io::IO, ::MIME"text/plain", v::MyVec)
print(io, "MyVec($(v.a), $(v.b))")
end
After evaluating the above, creating an instance and printing it uses the new method:
v = MyVec(3.14, 2.71)
print(v) # → MyVec(3.14, 2.71)
To verify that the correct method was chosen, you can use the introspection tool @which:
@which show(v)
# The output will indicate the method signature you just defined.
Similarly, you can inspect the generated LLVM to see that a specialized version exists:
@code_llvm show(v)
Trade‑offs and limitations
- Compile latency: The first time
showis called with aMyVecargument, Julia compiles the new method. In interactive work or short scripts this can cause a noticeable pause. - Over‑specialization: If you create many methods that differ only by tiny type variations (e.g., separate methods for
Float32andFloat64when the algorithm is identical), compile time and memory usage can grow. Keep the specialization granular enough to be useful but not excessive. - Method ambiguity: Adding a method that is not sufficiently specific can lead to ambiguous calls, which Julia will reject at definition time. Ensure your new method’s signature is more specific than any existing fallback.
Practical way to check the result: after defining the method, call show on an instance and confirm the output matches your format. Use @btime from the BenchmarkTools package to compare the time of the first call versus subsequent calls; you should see the first call include compilation overhead while later calls are faster.
Actionable closing
If you need a custom display for a type, follow these steps:
- Define your type.
- Import
Base.show(or the specificshowvariant you want to extend). - Add a method that specializes on your type and the desired I/O arguments.
- Verify with
@whichand, if performance matters, benchmark with@btime. - Keep an eye on compile latency; consider precompiling the method in a package’s
__init__if the latency is problematic for your workflow.
By leveraging multiple dispatch, you get extensible, high‑performance behavior without modifying Base, keeping your code composable and maintainable.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.