How to design LaTeX output?


#1

I write this long message because I am perplexed about the output
facilities in Julia, to the point that I have no idea how to proceed with
my design.

I want to write facilities for outputting in TeX/LaTeX my objects. I am
trying to port GAP code and my objects could be things like character
tables, polynomials, matrix of polynomials, etc… The main purpose of
LaTeX facilities would be to prepare presentations/papers from julia
computations.

With the philosophy I know, I would design this as follows:

  • a function stringlatex for each type, which would build the LaTeX output
    recursively — for instance a matrix of Laurent polynomials would give

    \begin{array}{cc}\\ x^{10}+1 & y-2\\ y^{-5}+1 & x-y\end{array}

    where stringlatex for a matrix calls itself stringlatex on each entry.

  • a function latex which would in jupyter display the LaTeX inline and in
    the Repl would call latex/xdvi or pdflatex/xpdf.

The main purpose of latex would be mainly to check the output from
stringlatex, but it could also serve as a display function sometimes.

It seems in julia print functions are the primordial form for output and
string functions are derived from them, which seems a little more
complicated than the other way:

  • from string you can derive print as write(string(x))

  • but from print to derive string you need more complex code as you can
    see by looking at the source of print_to_string

Note added later: I discovered that there is an sprint function which
can derive a string function from any print function: why is not
string defined as sprint(print,...)?

Looking at the source, it seems that everything is derived at the bottom
from show with some options — this is good: all formatting routines
have the same recursive logic, so this way they can share code. Thus I
explored how to pass options to show and I found two ways:

  • build an IOContext with a specific option, like LaTeX=>true

  • use a mime-type: show(io,"application/x-latex",x)

and here I got very confused: which I should use? What is the relationship
between IOContexts and mime-types?

These questions started me on exploring further the output facilities of
julia which left me even more confused.

From my background in the 15 programming languages I learned along the
years (I am 67) I see 3 basic forms of print/string functions which are
necessary in a language (of course there are other ones like
"export-to-latex" which is my current interest, but you may want also
"export-to-html" or “export-to-xxx” where xxx is anything you may think of:
R, Mathlab, Maple, Mathematica, etc…)

1- default output at the repl. This could have a ‘compact’ mode and a
’verbose’ mode.

2- A way to print a representation which can be read back — in the
context of julia this means deriving from an object x a string s such
that eval(parse(s))=x

3- A ‘display’ function which would show an object in the most enlightening
way possible. For example, for a character table it would be a big
square matrix with labels on the rows and columns (perhaps several rows
of labels on the columns for the class names, class sizes, powermap,
etc…).

An for each of these modes there would be a string function and a print
function.

There is a ‘display’ function in julia, which seems to fit my requirement
3, but I could not find anything which fits items 1 or 2.

I think a facility for 2 is needed in the context of metaprogramming (even of the
most naive sort: preparing text files containing data which can be read in
julia – I found that this is not as trivial as it seems: matrices
outputted by GAP as vectors of vectors can sometimes not be read in julia
depending on what is the last token before a line break).

  • I could not discover which function does the printing at the repl.

  • The correspondence between string and print functions does not seem to
    fit any coherent naming scheme: I could discover that ‘string’ is the
    string function for ‘print’ and ‘repr’ the string function for showall, but
    I could not find what are the string functions for ‘show’ or ‘showcompact’.

  • Also I was suprised that there is a ‘summary’ function which is a string
    function and not a print function contrary to the apparent philosophy of
    the other ones.

Excuse me for this long rant and if I do not use the proper terminology,
but the point is that I am here to learn.


#2

I only skimmed the post, sorry if I miss something, but
couldn’t you do something like

show(::IO, x^2-3x+4y^4*x)
4y^4*x + x^2 - 3x # parseable by Julia
show(::IO, ::MIME"text/plain", x^2-3x+4y^4*x)
4y⁴x + x² - 3x # Most readable
show(::IO, ::MIME"text/latex", x^2-3x+4y^4)
4y^4x + x^2 - 3x # parseable by LaTeX

and use the stringmime function to obtain a string version?


#3

Take a look at Latexify.jl.


#4

Ah! the output of show is parseable by julia? This then answers my point 2. Is this documented somewhere,
so that implementers of various types follow this guideline?

And you suggest that I should use a mime type instead of an IOContext. I am also confused about mime types: I saw in the source a “application/x-latex” but you use “text/latex”. What is the difference?


#5

Excuse me, but it seems to me that Latexify.jl is designed the wrong way. There is a cornucopia of functions
and it is not using options of show, precluding the possibility of sharing code with other output functions.


#6

Feel free to open an issue and share your expertise / what you’ve learned to improve it. MIME types have always confused me so some guidance to make this more widely applicable would be great. I’m sure it has at least a lot of tooling that could be used for the “right” design.


#7

By the way, is show(::IO, ::MIME"text/plain", your invention, or does it exist?
I get

 julia> show(STDOUT,MIME"text/plain",1)
 ERROR: MethodError: no method matching show(::Base.TTY, ::Type{MIME{Symbol("text/plain")}}, ::Int64)
 Closest candidates are:
   show(::IO, ::DataType) at show.jl:211
   show(::IO, ::MIME{Symbol("text/csv")}, ::Any) at datafmt.jl:707
   show(::IO, ::MIME{Symbol("text/tab-separated-values")}, ::Any) at datafmt.jl:708
   ..

#8

You’re passing the MIME{Symbol("text/plain")} type, but the relevant methods are defined for the instances of that (singleton) type. This isn’t something users will typically do by hand, but you can get the right behavior by actually instantiating that type:

julia> show(STDOUT, MIME"text/plain"(), 1)
1

#9

Thanks to you, I discovered (perhaps?) the function which does output to the Repl, which answers my
point 1: it seems to be
show(STDOUT, MIME"text/plain"(), 1)

thus I could construct the corresponding string function as

sprint((x,y)->show(x,MIME"text/plain"(),y),obj)

So for users to design a nice Repl display of their own type, one should add methods to this function. Is this documented somewhere? And you say it isn’t something to be done by hand: is there a shorter equivalent?


#10

Yep, relevant docs are:


#11

I read that but could not find (missed?) the two crucial statements that
show(STDOUT, MIME"text/plain"(), x) is what is used at the Repl,
and that the output of show(x) is julia-parseable.


#12

From the first link:

Technically, the REPL calls display(z) to display the result of executing a line, which defaults to show(STDOUT, MIME("text/plain"), z), which in turn defaults to show(STDOUT, z)

I don’t think the julia-parseable part is true in general (although generally an effort is made to make that be the case).


#13

Do I understand correctly that “default to” in the above sentence means:

  • If there is a method for display(z), use it
  • Otherwise if there is a method for show(STDOUT, MIME("text/plain"), z), use it
  • Otherwise use show(STDOUT,z)

#14

No. “Default to” is referring to what the fallback method does. For example, there is a fallback show(io::IO, ::MIME"text/plain", x) = show(io, x) method, which is why if you just define show(io, x) it will be called by show(io, "text/plain", x) unless you define a more specialized three-argument method.

Anyway, as explained in the docs, just overload show as needed.


#15

I’m working on a new package that outputs LaTeX by compiling usng latexmk and display with zathura PDF viewer. It works well for people in the terminal with tiling window manager.


#16

Latexify.jl does not implement the output formatting itself. That is taken care of by using the LatexString type from LatexStrings.jl (and in the latest version it uses some Base.Markdown.MD). These types both have overloads to Base.show with nice defaults for rendering/printing.

I find that to work pretty well, although it becomes somewhat problematic that Juno does not support MIME"text/latex".


#17

Thank you for explaining that and pointing to LatexStrings which is what I should have looked at for my design. And sorry for criticizing `Latexify’ – though I still think there are too many functions…


#18

No problems. Do you mean that Latexify.jl has too many functions internally, or too many presented to the user? Do you have ideas for improvements?


#19

I mean too many presented to the user. Some functionalities should be options, and the advantage of options is that you can pass them recursively to subroutines which gives great flexibility and allows to reuse more code. But to be honest I have not looked at your code in any detail so do not take my opinion too seriously.


#20

I have taken your critisism under advisment and opened an issue to gather opinions/advise before I move forward. Please feed free to weigh in (you and anyone who reads this).