Julia and Quarto: a match made in heaven? 🌤
A new way to publish science
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.
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:
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:
Just had my first go #quarto and I absolutely love the concept! Open-source and language agnostic - truly amazing work from @rstudio https://t.co/veCg7ywQ8v
— Patrick Altmeyer (@paltmey) October 29, 2021
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:
#RStats can someone explain to me what's the difference between {Quarto} and {RMarkdown}? I saw a tweet about Quarto and now I'm all confused … What gap is it supposed to fill?
— Erwin Lares (@lasrubieras) March 30, 2022
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:
- 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.
- I do most of my work in Quarto documents
.qmd
. - If you choose to also do that, make sure that the
.qmd
document has access to aPkg.jl
environment that hasIJulia
added.
Julia code cells can be added anywhere along with your plain text Markdown. They look like this:
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.10
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
@online{altmeyer2022,
author = {Altmeyer, Patrick},
title = {Julia and {Quarto:} A Match Made in Heaven? 🌤},
date = {2022-04-07},
url = {https://www.patalt.org/blog/posts/julia-and-quarto-a-match-made-in-heaven/},
langid = {en}
}