Displaying Rationals


#1

I have a small complaint about julia. I use all the time matrices of Rationals, like

> 10×10 Array{Rational{Int64},2}:
>  2//1  0//1   0//1   0//1  0//1   0//1   0//1   0//1   0//1   0//1
>  0//1  2//1   0//1   0//1  0//1   0//1   0//1   0//1   0//1   0//1
>  0//1  0//1   4//3  -2//3  2//3   0//1   0//1   2//3  -2//3  -2//3
>  0//1  0//1  -2//3   4//3  2//3   0//1   0//1   2//3  -2//3  -2//3
>  0//1  0//1   2//3   2//3  1//3   1//1   1//1   1//3   2//3   2//3
>  0//1  0//1   0//1   0//1  1//1   1//1  -1//1  -1//1   0//1   0//1
>  0//1  0//1   0//1   0//1  1//1  -1//1   1//1  -1//1   0//1   0//1
>  0//1  0//1   2//3   2//3  1//3  -1//1  -1//1   1//3   2//3   2//3
>  0//1  0//1  -2//3  -2//3  2//3   0//1   0//1   2//3   4//3  -2//3
>  0//1  0//1  -2//3  -2//3  2//3   0//1   0//1   2//3  -2//3   4//3

I find it perfectly acceptable that to input a Rational, you have
to write a//b. But I find pretty horrible that they are output the same
way. I would like to see:

10×10 Array{Rational{Int64},2}:
2 0    0    0   0  0  0   0    0    0
0 2    0    0   0  0  0   0    0    0
0 0  4/3 -2/3 2/3  0  0 2/3 -2/3 -2/3
0 0 -2/3  4/3 2/3  0  0 2/3 -2/3 -2/3
0 0  2/3  2/3 1/3  1  1 1/3  2/3  2/3
0 0    0    0   1  1 -1  -1    0    0
0 0    0    0   1 -1  1  -1    0    0
0 0  2/3  2/3 1/3 -1 -1 1/3  2/3  2/3
0 0 -2/3 -2/3 2/3  0  0 2/3  4/3 -2/3
0 0 -2/3 -2/3 2/3  0  0 2/3 -2/3  4/3

As it is, I have the feeling that I must change my glasses whenever I print
a matrix of Rationals. As I understood, you would just have to change (or to
write a new)

function Base.display(x::Rational)

to do the trick. Do people think this would be reasonable?


#2

It would be weird to not be able to copy them and have it keep the same type.


#3

I thought the function show and not the function display was the one where you should be able to parse back the output. Anyway you cannot copy as is the output: you would have to suppress the summary and add []. I certainly would not change the function show so you could still copy its output and get the same type.


#4

No, you should not overload display. Not only would this not affect output as part of a matrix, but the manual explicitly tells you not to overload display. To customize output, you use show.

One option would be to define a show method for Array{<:Rational}.

A more permanent solution would be to modify the Base display of Rational types. In particular, when a rational value is shown in an array, the array sets the :compact IO context attribute to true. It might be reasonable to show rationals as p/q for :compact=>true output. In particular, you could submit a JuliaLang/julia PR that modifies the show(io, ::Rational) method to something like:

function show(io::IO, x::Rational)
    show(io, numerator(x))
    print(io, get(io, :compact, false) ? "/" : "//")
    show(io, denominator(x))
end

With this change, arrays of rationals will use a single slash, but showing a single rational value in the REPL will still use a double slash. The only problem is that it would affect repr of arrays too, meaning the repr output would not be parseable to reproduce the input.

Another option would to have a different IO context attribute to suppress the double slash in rational values, which could be set by e.g. show(io::IO, ::MIME"text/plain", X::AbstractArray). Maybe there should be a more general IO context attribute for whether the output needs to be parseable?

I encourage you to try submitting a PR for this, which should be relatively easy (just the code patch above and a test). I can’t guarantee that it will be accepted, but I think there is a reasonable chance that some variant of this change could go in, and it hopefully would be a good experience for you in any case.


#5

But when a rational object is converted to a string, I hope it still retains the // because, for example in my packages it is needed to parse rational julia objects into rational values in another programming language, and the behavior of / and // is supposed to be different when parsing the text of Julia code.


#6

That’s why we might want an IO context attribute to indicate whether the show output should be parseable etc.


#7

I would like to only modify the display of Rational arrays in the REPL, nothing else since indeed you
want that all functions usually used for output like show(x) and print(x) give parseable output. So I guess
this needs to follow your suggestion to use an IOContext set by

show(io::IO, ::MIME"text/plain", X::AbstractArray)

To have an uncluttered display like I showed above, I would need to modify slightly your code to

   show(io, numerator(x))
   if  get(io, :compact,true) 
        if denominator(x!=1)
           print(io,"/")
           show(io,denominator(x))
        end
   else
       print(io, "//")
       show(io, denominator(x))
   end

Do you think that :compact is the right io property or another one should be used?