Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to overload Base.show for custom array types?

Tags:

julia

Suppose I make my own custom vector type with it's own custom show method:

struct MyVector{T} <: AbstractVector{T}
    v::Vector{T}
end

function Base.show(io::IO, v::MyVector{T}) where {T}
    println(io, "My custom vector with eltype $T with elements")
    for i in eachindex(v)
        println(io, "  ", v.v[i])
    end
end

If I try making one of these objects at the REPL I get unexpected errors related to functions I never intended to call:

julia> MyVector([1, 2, 3])
Error showing value of type MyVector{Int64}:
ERROR: MethodError: no method matching size(::MyVector{Int64})
Closest candidates are:
  size(::AbstractArray{T,N}, ::Any) where {T, N} at abstractarray.jl:38
  size(::BitArray{1}) at bitarray.jl:77
  size(::BitArray{1}, ::Integer) at bitarray.jl:81
  ...
Stacktrace:
 [1] axes at ./abstractarray.jl:75 [inlined]
 [2] summary(::IOContext{REPL.Terminals.TTYTerminal}, ::MyVector{Int64}) at ./show.jl:1877
 [3] show(::IOContext{REPL.Terminals.TTYTerminal}, ::MIME{Symbol("text/plain")}, ::MyVector{Int64}) at ./arrayshow.jl:316
 [4] display(::REPL.REPLDisplay, ::MIME{Symbol("text/plain")}, ::Any) at /Users/mason/julia/usr/share/julia/stdlib/v1.3/REPL/src/REPL.jl:132
 [5] display(::REPL.REPLDisplay, ::Any) at /Users/mason/julia/usr/share/julia/stdlib/v1.3/REPL/src/REPL.jl:136
 [6] display(::Any) at ./multimedia.jl:323
 ...

Okay, whatever so I'll implement Base.size so it'll leave me alone:

julia> Base.size(v::MyVector) = size(v.v)

julia> MyVector([1, 2, 3])
3-element MyVector{Int64}:
Error showing value of type MyVector{Int64}:
ERROR: getindex not defined for MyVector{Int64}
Stacktrace:
 [1] error(::String, ::Type) at ./error.jl:42
 [2] error_if_canonical_getindex(::IndexCartesian, ::MyVector{Int64}, ::Int64) at ./abstractarray.jl:991
 [3] _getindex at ./abstractarray.jl:980 [inlined]
 [4] getindex at ./abstractarray.jl:981 [inlined]
 [5] isassigned(::MyVector{Int64}, ::Int64, ::Int64) at ./abstractarray.jl:405
 [6] alignment(::IOContext{REPL.Terminals.TTYTerminal}, ::MyVector{Int64}, ::UnitRange{Int64}, ::UnitRange{Int64}, ::Int64, ::Int64, ::Int64) at ./arrayshow.jl:67
 [7] print_matrix(::IOContext{REPL.Terminals.TTYTerminal}, ::MyVector{Int64}, ::String, ::String, ::String, ::String, ::String, ::String, ::Int64, ::Int64) at ./arrayshow.jl:186
 [8] print_matrix at ./arrayshow.jl:159 [inlined]
 [9] print_array at ./arrayshow.jl:308 [inlined]
 [10] show(::IOContext{REPL.Terminals.TTYTerminal}, ::MIME{Symbol("text/plain")}, ::MyVector{Int64}) at ./arrayshow.jl:345
 [11] display(::REPL.REPLDisplay, ::MIME{Symbol("text/plain")}, ::Any) at /Users/mason/julia/usr/share/julia/stdlib/v1.3/REPL/src/REPL.jl:132
 [12] display(::REPL.REPLDisplay, ::Any) at /Users/mason/julia/usr/share/julia/stdlib/v1.3/REPL/src/REPL.jl:136
 [13] display(::Any) at ./multimedia.jl:323
 ...

Hmm, now it wants getindex

julia> Base.getindex(v::MyVector, args...) = getindex(v.v, args...)

julia> MyVector([1, 2, 3])
3-element MyVector{Int64}:
 1
 2
 3

What? That wasn't the print formatting I told it to do! what's going on here?

like image 771
Mason Avatar asked Nov 20 '19 19:11

Mason


1 Answers

The problem is that in julia, Base defines a method Base.show(io::IO ::MIME"text/plain", X::AbstractArray) which is actually more specific than the Base.show(io::IO, v::MyVector) for the purposes of display. This section of the julia manual describes the sort of custom printing that AbstractArray uses. So if we want to use our custom show method, we instead need to do

julia> function Base.show(io::IO, ::MIME"text/plain", v::MyVector{T}) where {T}
           println(io, "My custom vector with eltype $T and elements")
           for i in eachindex(v)
               println(io, "  ", v.v[i])
           end
       end

julia> MyVector([1, 2, 3])
My custom vector with eltype Int64 and elements
  1
  2
  3

See also: https://discourse.julialang.org/t/extending-base-show-for-array-of-types/31289

like image 92
Mason Avatar answered Nov 10 '22 10:11

Mason