Custom show for a struct that contains matrix

Suppose I have a struct

struct Box
   size::Int
   matrix::Matrix{ComplexF64}
end

and my aim is to implement a custom show method for a Box that will display its matrix in a nice rectangle form (not printing all the elements in a row). I would expect that I have to write smth like this:

function Base.show(io::IO, box::Box)
   println(io, "Box of size $(box.size):"
   println(io, " matrix:")
   print(io, box.matrix)
end

but it prints all the matrix elements in one row. Using display(box.matrix) inside show(io::IO, box::Box) prints the matrix in a nice rectangle form, but it does it two times for some reason…

Your code with display works fine for me (though it’s probably not ideal we don’t use io there, you could use Base.show(io, "text/plain", box.matrix) instead):

struct Box
   size::Int
   matrix::Matrix{ComplexF64}
end

function show(io::IO, box::Box)
   println(io, "Box of size $(box.size):")
   println(io, " matrix:")
   display(box.matrix)
end

box = Box(3, rand(ComplexF64, 3, 3))
julia> show(stdout, box)
Box of size 3:
 matrix:
3Ă—3 Matrix{ComplexF64}:
 0.110489+0.490646im  0.216738+0.506696im    0.806984+0.535515im
 0.569361+0.497763im  0.990934+0.791805im    0.344692+0.786712im
 0.662557+0.48268im   0.550246+0.0305454im  0.0693276+0.143775im

I don’t know why would you get something different, but perhaps you’re somehow accidentally returning box.matrix from show?

julia> function show(io::IO, box::Box)
          println(io, "Box of size $(box.size):")
          println(io, " matrix:")
          display(box.matrix); box.matrix
       end
show (generic function with 1 method)

julia> show(stdout, box)
Box of size 3:
 matrix:
3Ă—3 Matrix{ComplexF64}:
 0.110489+0.490646im  0.216738+0.506696im    0.806984+0.535515im
 0.569361+0.497763im  0.990934+0.791805im    0.344692+0.786712im
 0.662557+0.48268im   0.550246+0.0305454im  0.0693276+0.143775im
3Ă—3 Matrix{ComplexF64}:
 0.110489+0.490646im  0.216738+0.506696im    0.806984+0.535515im
 0.569361+0.497763im  0.990934+0.791805im    0.344692+0.786712im
 0.662557+0.48268im   0.550246+0.0305454im  0.0693276+0.143775im

julia> show(stdout, box);
Box of size 3:
 matrix:
3Ă—3 Matrix{ComplexF64}:
 0.110489+0.490646im  0.216738+0.506696im    0.806984+0.535515im
 0.569361+0.497763im  0.990934+0.791805im    0.344692+0.786712im
 0.662557+0.48268im   0.550246+0.0305454im  0.0693276+0.143775im

Oh, right. Calling show(stdout, box) indeed prints it only once. And after printing just box in the Julia REPL prints it only once. However, in VScode editing window, when I press Shift+Enter to evaluate the command box, it prints it twice in the REPL for some reason:

Box of size 3:
 matrix:
3Ă—3 Matrix{ComplexF64}:
  0.207312+0.832938im  0.656108+0.173641im   0.661501+0.384587im
  0.983555+0.684514im  0.292562+0.617771im  0.0917733+0.096517im
 0.0218214+0.730983im  0.986447+0.37221im    0.095009+0.715964im

3Ă—3 Matrix{ComplexF64}:
  0.207312+0.832938im  0.656108+0.173641im   0.661501+0.384587im
  0.983555+0.684514im  0.292562+0.617771im  0.0917733+0.096517im
 0.0218214+0.730983im  0.986447+0.37221im    0.095009+0.715964im

But maybe it is the problem with VScode…

In my VS Code Shift + Enter on a line containing only the box variable prints the same as println(box):

Box(3, ComplexF64[0.17059151873830936 + 0.6467902853991271im 0.5516890187413159 + 0.16840124279428315im 0.04774010114262217 + 0.03149348949086228im; 0.16131392705103242 + 0.03850134950744866im 0.42382508938133245 + 0.2807228527058967im 0.32199193022264194 + 0.06329937630082594im; 0.6228329149783915 + 0.3768341650812741im 0.7527126699625651 + 0.16822047117462158im 0.36431680664443533 + 0.469961437362322im])

(and Shift + Enter on show(stdout, box) works as intended, with a single pretty print).

Perhaps you have installed other relevant extensions apart from the official Julia extension, or changed some settings of the latter, altering VS Code’s behaviour when displaying variables?

If you Shift+Enter on a line box;, do you still get a double print?

I have only one Julia extension installed in VScode.

If I Shift+Enter on a line box; I get nothing printed. If I Shift+Enter on box gives me double pretty print, and Shift+Enter on println(box) gives single pretty print…

But what if you extend the show function from the Base module using function Base.show(io::IO, box::Box)?

Oh yeah, that would make more sense. (I see you added the Base. in an edit, presumably after I started typing my first reply, so I always worked with the original (implicit) Main. version.)
With the Base.show version I do get the nice prints (using box, show(stdout, box) or println(box)), but still only one (both for scripts and Jupyter notebooks).

It seems that this problem arises due to not specifying the IO in the display method. (I am using Apple M1 machine, if it is important…) Suppose I have the following code:

struct Box
    size::Int
    matrix::Matrix{ComplexF64}
 end
 
 function Base.show(io::IO, box::Box)
    println(io, "Box of size $(box.size):")
    println("111")
    println(io, " matrix:")
    println("222")
    display(box.matrix)
 end

box = Box(3, rand(ComplexF64, 2, 2))

Then the Shift+Enter command applied to box gives

Box of size 3:
111
 matrix:
222
2Ă—2 Matrix{ComplexF64}:
 0.305076+0.347138im  0.456306+0.974372im
 0.169632+0.881314im  0.435036+0.345573im

111
222
2Ă—2 Matrix{ComplexF64}:
 0.305076+0.347138im  0.456306+0.974372im
 0.169632+0.881314im  0.435036+0.345573im

Would you know maybe another way than display to pretty print a matrix which takes IO as an argument?

Well, you could try Base.show(io, "text/plain", box.matrix).

2 Likes

Ooh, awesome, it works well now. I should probably read about MIMEs in the Julia docs and what they are used for… Thank you!!

You actually suggested this in your first answer, but I overlooked it :frowning:

1 Like

In particular, the show method you are defining should probably be the 3-argument show, not the 2-argument show:

function Base.show(io::IO, mime::MIME"text/plain", box::Box)
    ...
end

and then call show(io, mime, box.matrix). The reason is that you are defining a multi-line verbose representation designed for human consumption, whereas the 2-argument show should normally be a single-line and compatible with how you enter the Box in Julia (this is the default).

This is all described in the manual’s chapter on custom pretty-printing. You might also want to check out the new summary of output functions that will appear in the next version of the manual.

2 Likes

Thank you!