Julia and Quarto: a match made in heaven? 🌤

A new way to publish science

scientific publishing
Quarto
Julia
An opinionated, practical review celebrating the open-source community. I discuss why Quarto is nothing short of revolutionary and how I’ve been using it with Julia.
Published

April 7, 2022

Julia and Quarto: a perfect match.

Does your work involve research, coding, writing and publishing? If so, then chances are that you often find yourself bouncing back and forth between different open-source text editors, IDEs, programming languages and platforms depending on your current needs. Using a diverse set of tools is reasonable, because there typically is no single perfect approach that solves all our problems. For example, interactive notebooks like Jupyter are useful for working with code and communicating it to others, but they are probably not anyone’s first choice for producing a scientific article. Similarly, Beamer presentations can be useful for presenting science in a standardized fashion, but they are the very opposite of interactive and look incredibly boring.

As much as the great variety of free tools deserves being celebrated, all this bouncing back and forth can be really tiring. What if there was a single tool, an engine that can turn your work into all kinds of different outputs? I mean literally any output you can think of: Markdown, HTML, PDF, LateX, ePub, entire websites, presentations (yes, also Beamer if you have to), MS Word, OpenOffice, … the list goes on. All of that starting from the same place: a plain Markdown document blended with essentially any programming language of your choice and a YAML header defining your output. This tool now exists and it goes by the name Quarto.

In this short blog post I hope to convince you that Quarto is the only publishing engine you will ever need. What I am definitely not going to tell you is which IDE, text editor or programming language you should be using to actually produce your work. Quarto does not care about that. Quarto is here to make your life a bit easier (and by ‘a bit’ I mean a whole lot). Quarto is nothing less but a revolution for scientific publishing.

To put this all in some context (well, my context), I will now tell you a bit about what has led me to making such bold claims about yet another open-source tool.

Hold up?! Wasn’t this supposed to be about Julia and Quarto?

Yes! But it’s worth noting that a lot of the benefits that Quarto brings have been available to R users for many years, thanks to the amazing work of many great open-source contributors like @xieyihui. Julia was the main reason for me to branch out of this comfortable R bubble as I describe below. That said, if you are a Julia user who really couldn’t care less about my previous experiences with R Markdown, this is a good time to skip straight ahead to Section 2. By the way, if you haven’t clicked on that link, here’s a small showcase demonstrating how it was generated. It shows easy it is to have everything well organised and connected with Quarto.

Cross-referencing

There is a standard recipe for generating cross-references in Quarto. The example below involves a section cross-reference.

If you are a Julia user that really couldn't care less about my previous experiences with R Markdown, this is a good time to skip straight ahead to @sec-match.

## Quarto and Julia - a perfect match {#sec-match}

There is actually a comprehensive 8-step guide explaining how to achieve something similar in MS Word, but personally I wouldn’t go there. Anyway, take your pick:

  1. 🔴 Go there.
  2. 🟢 Move on to Section 1 #safespace.

A comfortable bubble 🎈

For many years I have used R Markdown for essentially anything work-related. As an undergraduate economics student facing the unfortunate reality that people still teach Stata, I was drawn to R. This was partially because R has a great open-source community and also partially because Stata. Once I realised that I would be able to use R Markdown to write up all of my future homework assignments and even my thesis, I never looked back. MS Word was now officially dead to me. Overleaf was nothing more than a last resort if everyone else in my team insisted on using it for a group project. Being able to write my undergraduate dissertation in R Markdown was a first truly triumphant moment. Soon after that I would also try myself at Shiny, produce outputs in HTML and build entire websites through blogdown. And all of that from within R Studio involving R and Markdown and really not much else. During my first professional job at the Bank of England I was reluctant to use anything other than R Markdown to produce all of my output. Luckily for me, the Bank was very much heading in that same direction at the time and my reluctance was not perceived as stubbornness, but actually welcome (at least I hoped so).

Cracks in the bubble 🧨

Soon though, part of me felt a little boxed in. For any work that required me to look outside of the R bubble, I knew I might also have to give up a very, very comfortable work environment and my productivity would surely take a hit. During my master’s in Data Science, for example, the mantra was very much “Python + Jupyter or die”. Through reticulate and R Studio’s growing support for Python I managed to get by without having to leave my bubble too often. But reticulate always felt a little clunky (sorry!) and some professors were reluctant to accept anything other than Jupyter notebooks. Even if others had not perceived it that way in the past, I certainly started to feel that I might just be a little too attached the beautiful bubble that R Studio had created around me.

Enter: Julia 💣

Then there was Julia: elegant, fast, pure, scientific and - oh my REPL! - those beautiful colors and unicode symbols. The stuff of dreams, really! Geeky dreams, but dreams nonetheless. I had once before given Julia a shot when working with high-frequency trade data for a course in market microstructure. This was the first time R really revealed its limitations to me and my bubble nearly burst, but thanks to data.table and Rcpp I managed to escape with only minor bruises. Still, Julia kept popping up, teasing me whenever I would work on some Frakenstein-style C++ code snippets that would hopefully resolve my R bottlenecks. I actually enjoyed mixing some C++ into my R code like I did here, but the process was just a little painful and slow. But wouldn’t learning all of Julia take even more time and patience? And what about my dependence on R Markdown?

Julia bursts my bubble 💥

As I started my PhD in September 2021, I eventually gave in. New beginnings - time to suck it up! If it meant that I’d have to use Jupyter notebooks with Julia, so be it! And so I was off to a somewhat bumpy start that would have me bouncing back and forth between trying to make Julia work in R Studio (meh), setting up Jupyter Lab (meeeh), just using the Julia REPL because “the REPL is all you need” (nope) and even struggling with Vim and Emacs. Then there was also Pluto.jl, of course, which admittedly looks amazing! But it also looks very much tailored to Julia and (I believe) the number of different output formats you can produce is still very limited. Eventually, I settled for VSCode in combination with Jupyter notebooks. As much as I dreaded the latter, Jupyter is popular, arguably versatile and supports both R and Julia. This setup worked well enough for me, but it still definitely fell short of the breeze that R Studio had always provided. One thing that really bugged me, for example, was the fact that the IJulia kernel was not accessible from the Julia REPL. Each notebook would have its own environment, which could only be accessed through the notebook. In R Studio the interaction between R Markdown and the console is seamless, as both have access to the same environment variables.

Enter: Quarto ❤️‍🩹

Around the same time that I started using Julia, I read about Quarto for the first time. It looked … great! Like a timely little miracle really! But also … unfinished? Definitely experimental at the time. I loved the idea though and in a footnote somewhere on their website it said that the project was supported by R Studio which I took as a very good sign. So I decided to at least give it a quick try and built a small (tiny) website summarising some of the literature I had read for my PhD:

This was a first very pleasant encounter with Quarto, arguable even smoother than building websites in blogdown. As for working with Julia though, I had made up my mind that VSCode was the way to go and at the time there was no Quarto extension (there is now). There was also little in terms of communication about the project by R Studio, probably because things were really still in the early development stages. I was hopeful that eventually Quarto would enable me to emulate the R Studio experience in VS Code, but for now things were not quite there yet.

Quarto keeps growing 🤞

Since I was now working with VSCode + Jupyter and since Quarto supports Jupyter as well as all of my old R Markdown work, my next little Quarto project involved turning my old blogdown-powered blog into a Quarto-powered blog. This was not strictly necessary, as I could always export my new Jupyter notebooks to HTML and let blogdown do the rest. But it did streamline things a little bit and the default Quarto blog theme - you are staring at it - is actually 🔥. I also did not have to feel guilty towards @xieyihui about leaving blogdown, because unsurprisingly he is on the Quarto team. As I was working on this little project I started noticing that the Quarto website was updated regularly and responses to issues I opened like this one were answered very swiftly. Clearly, things were moving and they were moving fast. More recently, the news about Quarto has been spreading and it’s left some folks as confused and amazed as I was, when I first heard about it:

This is why finally I’ve decided I should write a brief post about how and why I use Quarto. Since I have been working mostly with Julia for the past couple of months, I’ve chosen to focus on the interaction between Quarto and Julia. Coincidentally, yesterday was also the first time I saw a guide dedicated to Julia on the Quarto website, so evidently I am not the only one interested in that marriage. This also means that there really is not too much left for me to talk about now, since Quarto’s documentation is state-of-the-art. But a few bits and pieces I mention below might hopefully still be useful or at least some food for thought.

Quarto and Julia: a perfect match 💙💜💚

While what follows may be relevant to other programming languages, my main goal for this last section is to flag Quarto to the Julia community. In any case, #rstats folks have been using R and Python in R Markdown documents for a while now and won’t need much of an introduction to Quarto. As for Python aficionados, I can only recommend to give Quarto a shot (you will still be able to use Jupyter notebooks).

Working with VSCode, Quarto and Julia

The very article you are reading right now was composed in a Quarto document. These documents feel and look very much like standard Julia Markdown documents, but you can do a lot more with them. You can find the source code for this and other documents presented in this blog here.

To get you started, here is my current setup combining VSCode, Quarto and Julia:

  1. VSCode extensions: in addition to the Julia extension you will need the Quarto extension. In addition, the YAML extension and some extension to preview Markdown docs would be helpful. I am not sure if Markdown Julia and Jupyter are strictly necessary, but it won’t hurt.
  2. I do most of my work in Quarto documents .qmd.
  3. If you choose to also do that, make sure that the .qmd document has access to a Pkg.jl environment that has IJulia added.

Julia code cells can be added anywhere along with your plain text Markdown. They look like this:

```{julia}
using Pkg
Pkg.add("CounterfactualExplanations")
```

Contrary to Jupyter notebooks, executing this code cells will start a Julia REPL in VSCode. I find this very helpful, because it lets me fiddle with anything I have created inside the Quarto notebook without having to click into cells all the time. Quarto comes with great support for specifying code executing options. For example, for the code below I have specified #| echo: true in order for the code to be rendered. The code itself is the code I actually used to build the animation above (heavily borrowed from this Javis.jl tutorial).

#| echo: true
using Javis, Animations, Colors

size = 600
radius_factor = 0.33

function ground(args...)
    background("transparent")
    sethue("white")
end

function rotate_anim(idx::Number, total::Number) 
    distance_circle = 0.875
    steps = collect(range(distance_circle,1-distance_circle,length=total))
    Animation(
        [0, 1], # must go from 0 to 1
        [0, steps[idx]*2π],
        [sineio()],
    )
end

translate_anim = Animation(
    [0, 1], # must go from 0 to 1
    [O, Point(size*radius_factor, 0)],
    [sineio()],
)

translate_back_anim = Animation(
    [0, 1], # must go from 0 to 1
    [O, Point(-(size*radius_factor), 0)],
    [sineio()],
)

julia_colours = Dict(
    :blue => "#4063D8",
    :green => "#389826",
    :purple => "#9558b2",
    :red => "#CB3C33"
)
colour_order = [:red, :purple, :green, :blue]
n_colours = length(julia_colours)
function color_anim(start_colour::String, quarto_col::String="#4b95d0")
    Animation(
        [0, 1], # must go from 0 to 1
        [Lab(color(start_colour)), Lab(color(quarto_col))],
        [sineio()],
    )
end

video = Video(size, size)

frame_starts = 1:10:40
n_total = 250
n_frames = 150
Background(1:n_total, ground)

# Blob:
function element(; radius = 1)
    circle(O, radius, :fill) # The 4 is to make the circle not so small
end

# Cross:
function cross(color="black";orientation=:horizontal)
    sethue(color)
    setline(10)
    if orientation==:horizontal
        out = line(Point(-size,0),Point(size,0), :stroke)
    else
        out = line(Point(0,-size),Point(0,size), :stroke)
    end
    return out
end

for (i, frame_start) in enumerate(1:10:40)

    # Julia circles:
    blob = Object(frame_start:n_total, (args...;radius=1) -> element(;radius=radius))
    act!(blob, Action(1:Int(round(n_frames*0.25)), change(:radius, 1 => 75))) # scale up
    act!(blob, Action(n_frames:(n_frames+50), change(:radius, 75 => 250))) # scale up further
    act!(blob, Action(1:30, translate_anim, translate()))
    act!(blob, Action(31:120, rotate_anim(i, n_colours), rotate_around(Point(-(size*radius_factor), 0))))
    act!(blob, Action(121:150, translate_back_anim, translate()))
    act!(blob, Action(1:150, color_anim(julia_colours[colour_order[i]]), sethue()))

    # Quarto cross:
    cross_h = Object((n_frames+50):n_total, (args...) -> cross(;orientation=:horizontal))
    cross_v = Object((n_frames+50):n_total, (args...) -> cross(;orientation=:vertical))
end

render(
    video;
    pathname = joinpath(www_path, "intro.gif"),
)

Working with Documenter.jl and Quarto

An interesting application of Quarto in the Julia ecosystem is package documentation. This is of course best done using Documenter.jl and fortunately the two play nicely with each other, since both share a common ground (Markdown). Their interaction is perhaps best demonstrated through this Julia library I recently developed: CounterfactualExplanatinos.jl. On there you will find lot of Julia scripts *.jl under src/ and test/, as well as many Markdown .md and Quarto documents .qmd under docs. I wrote the package documentation in the Quarto documents, rendered documents individually through quarto render [doc].qmd and then fed the resulting Markdown documents to Documenter.jl as always.

Below is my standard YAML header for those Quarto documents:

format: 
  commonmark:
    variant: -raw_html
    wrap: none
    self-contained: true
crossref:
  fig-prefix: Figure
  tbl-prefix: Table
bibliography: https://raw.githubusercontent.com/pat-alt/bib/main/bib.bib
output: asis
execute: 
  echo: true
  eval: false
jupyter: julia-1.7

You can see that it points to Bibtex file I host on another Github repository. This makes it very easy to generate citations and references for the rendered Markdown documents, that also show up in the docs (e.g. here). Unfortunately, cross-referencing only partially works, because it relies on auto-generated HTML and Documenter.jl expects this to be passed in blocks. Choosing variant: -raw_html is only a workaround as I have discussed here. Ideally, Documenter.jl would just accept HTML documents rendered from Quarto, but currently only Markdown documents are accepted by make_docs. Still, if anything this workaround is a nice gimmick that extends the default Documenter.jl functionality, without any hassle involved. Hopefully, this can be improved in the future.

Using Quarto for JuliaCon Proceedings

Another very good use-case for Quarto involves actual scientific publications in journals such as JuliaCon Proceedings. The existing submission process is tailored towards reproducibility and actually involves reviews directly on GitHub, which is fantastic. But currently only submissions in TeX format are accepted, which is not so great. Using Quarto would not only streamline this process further, but also open the JuliaCon Proceedings Journal up to publishing content in different output formats. Quarto docs could be used to still render the traditional PDF. But those same documents could also be used to create interactive versions in HTML. Arguably, the entire journal could probably be built through Quarto.

Wrapping up 🎗

In this post I wanted to demonstrate that Quarto might just be the next revolution in scientific publishing. In particular, I hope I have managed to demonstrate its appeal to the Julia community, which I am proud to be part of now that I have managed to branch out of my old R bubble. Please let me hear your thoughts and comments below!

Citation

BibTeX citation:
@online{altmeyer2022,
  author = {Altmeyer, Patrick},
  title = {Julia and {Quarto:} A Match Made in Heaven? 🌤},
  date = {2022-04-07},
  url = {https://www.paltmeyer.com/blog//blog/posts/julia-and-quarto-a-match-made-in-heaven},
  langid = {en}
}
For attribution, please cite this work as:
Altmeyer, Patrick. 2022. “Julia and Quarto: A Match Made in Heaven? 🌤.” April 7, 2022. https://www.paltmeyer.com/blog//blog/posts/julia-and-quarto-a-match-made-in-heaven.