ob-julia: high quality julia org-mode support
+Table of Contents
+-
+
- 1. ob-julia +
- 2. How it works +
- 3. Implemented features
+
-
+
- 3.1. Session (
:session none
,:session
,:session session-name
)
+ - 3.2. Async (
:async
:async yes
,:async t
)
+ - 3.3. Variables input (
:var
), Standard output + +
+ - 3.4. Directory (
:dir
)
+ - 3.5. Error management +
- 3.6. Using (
:using
) and Import (:import
)
+ - 3.7. Results (
:results output
,:results file
, )
+ - 3.8. Supported Types +
- 3.9. File output & Inlining + + +
+ - 3.1. Session (
- 4. Issues and Missing features +
- 5. Credits +
1 ob-julia
++See 3 for more details. +
+2 How it works
+-
+
- Code block is saved to a temporary file (under /tmp) +
- Decide whether we need to start a new julia process or not
+
-
+
- If session is "none", don't use a session (code under /tmp will
+be passed to
julia -L initfile src-block.jl
). This does not +require ess. The command is customized by +org-babel-julia-command
)
+ - If session is nil, use default session name
+(customized by
org-babel-julia-default-session
)
+ - If session has a values, use it's name +
+ - If session is "none", don't use a session (code under /tmp will
+be passed to
- Check if we want to use a session or not. Check if the session +exists. Start the session accordingly. +
- Is the evaluation async?
+
-
+
- YES:
+
-
+
- Register a filter function +
- Return immediately, printing a ref to the evaluation.
+The ref is in the form:
julia-async:$uuid:$tmpfile
+-
+
- uuid: identifier of this evaluation, used to find out where to +insert again results +
- tmpfile: the path where the output will be saved. This is
+useful both for debugging purposes and so that we do not need
+to store an object that maps computations to files. The
+process-filter look for the uuid, and then inserts
$tmpfile
.
+
+
+ - NO: Run the code, wait for the results. Insert the results. +
+ - YES:
+
3 Implemented features
+3.1 Session (:session none
, :session
, :session session-name
)
+
+By default code is executed without a session. The advantage is that
+you do not requires emacs-ess
to run julia code with ob-julia. But
+sessions (especially for julia, where speed comes from compiled code)
+are available. The same behaviour is obtained by setting the :session
+header to none
.
+
+You can enable sessions with the :session
argument. Without
+parameters, the session used is named after
+org-babel-julia-default-session
(*julia*
by default). With a
+parameter, the name is earmuffed (a star is prepended and appended).
+
+The REPL is kept sane. There's no cluttering (you don't see all the
+code executed that is required by ob-julia to have results), history
+is preserved (you can C-S-up
to see the list of org-src block
+evaluated), and results are shown (this can be customized by
+org-babel-julia-silent-repl
).
+
3.2 Async (:async
:async yes
, :async t
)
+
+Async works both with and without :session
.
+
+The best combination of features is combining session with
+:async
. Async allows code evaluation in background (you can continue
+using emacs while julia compute results).
+
+You can change buffer or even narrow it while the evaluation +completes: the result is added automatically as soon as julia +finishes. Multiple evaluation are queued automatically (thanks to +ess). Cache is supported (evaluating the same code-block twice does +not re-trigger evaluation, even if the code is still running). +
+ +
+It's not possible to have async blocks with :results silent
. I'm
+currently using this to distinguish between active src block and
+variable resolution (when a :var
refer to an async block, the block
+cannot be executed asynchonously. So we need to distinguish between
+the two. This is the only way I was able to find, if you know better
+please tell me).
+
sleep(1)
+"It works!"
+
++It works! ++ + +
sleep(1)
+"It works!"
+
++It works! ++ + +
+Here the same, without the session +
+sleep(1)
+"It works!"
+
++It works! ++ + +
sleep(1)
+"It works!"
+
++It works! ++ + + +
+Asynchronous evaluation is automatically disabled on export, or when a
+code block depends on one (:var
)
+
3.3 Variables input (:var
), Standard output
+3.3.1 Inputs
++Those are example inputs that will be used later, to check whether +import+export pipeline works as expected. +
+ ++A table +
+ +a | +b | +
---|---|
1 | +1 | +
2 | +2 | +
+ | + |
4 | +4 | +
+A matrix (no hline) +
+ +1 | +2 | +3 | +4 | +
1 | +2 | +3 | +4 | +
+A column +
+ +1 | +
2 | +
3 | +
4 | +
+A row +
+ +1 | +2 | +3 | +4 | +
+A list +
+ +-
+
- 1 +
- 2 +
- 3 +
- 4 +
-
+
- Table
+++++ +table +
++ + +
+ ++ + ++ + + + + + +a +b ++ + +1 +1 ++ + +2 +2 ++ + ++ + +4 +4 ++As you can see, the table automatically adds the hline after the +header. This is a heuristic that might fail (might be triggered for +matrix, might not trigger on tables), so you can manually +force/disable it with the
+ +:results table
or:results matrix
param. +++ +table +
++ + +
++ + ++ + + + + +a +b ++ + +1 +1 ++ + +2 +2 ++ + ++ + +4 +4 +
+
+ - Row
++++Column, Rows, and Matrix export works just fine (tests in session sync, session async +and without session). +
+ +++ +row +
++ + +
+ ++ + ++ + + + + + + + + +1 +2 +3 +4 +++ +row +
++ + +
+ ++ + ++ + + + + + + + + +1 +2 +3 +4 +++ +row +
++ + +
++ + ++ + + + + + + + + +1 +2 +3 +4 +
+
+ - Column
++++Works both with synchronous evaluation +
+ +++ +column +
++ + +
+ ++ + ++ + + +1 ++ + +2 ++ + +3 ++ + +4 ++asynchronous evaluation +
+ +++ +column +
++ + +
+ ++ + ++ + + +1 ++ + +2 ++ + +3 ++ + +4 ++and without a session +
+ +++ +column +
++ + +
++ + ++ + + +1 ++ + +2 ++ + +3 ++ + +4 +
+
+ - Matrix
++++Sync +
+ +++ +matrix +
++ + +
+ ++ + ++ + + + + + + + + +1 +2 +3 +4 ++ + +1 +2 +3 +4 ++Just like for tables, you can control header hline line with the +results param. +
+ +++ +matrix +
++ + +
+ ++ + ++ + + + + + + + + + +1 +2 +3 +4 ++ + +1 +2 +3 +4 ++Async +
+ +++ +matrix +
++ + +
+ ++ + ++ + + + + + + + + +1 +2 +3 +4 ++ + +1 +2 +3 +4 ++No session +
+ +++ +matrix +
++ + +
++ + ++ + + + + + + + + + +1 +2 +3 +4 ++ + +1 +2 +3 +4 +
+
+ - List
++++List are parsed as columns +
+ +++ +list +
++
+:results list
return the list (just like R). It's not perfect with +++ +list +
+-
+
- (1) +
- (2) +
- (3) +
- (4) +
+
+ - Table
++++There are two ways in which tables can be passed to Julia: +
+-
+
- Array{NamedTuple} +
- Dictionary +
+I like the NamedTuple approach, but if you don't like it you can +customize the variable
+ +org-babel-julia-table-as-dict
. In both cases, +if you :import DataFrames, you can construct a DataFrame from both. ++TOOD: I miss the julia code for printing Array{NamedTuple}. +
+ +++ +table +
++ + +
+ ++ + ++ + + + + + +a +b ++ + +1 +1 ++ + +2 +2 ++ + ++ + +4 +4 ++Also, it's nice that a single NamedTuple can represent a table: +
+++ +table[2] +
++ + +
++ + ++ + + + + + +a +b ++ + +2 +2 +
+
3.4 Directory (:dir
)
++Each source block is evaluated in it's :dir param +
+ +pwd() ++
+/tmp ++ + +
pwd() ++
+/ ++ + +
+If unspecified, the directory is session's one +
+pwd() ++
+/home/nixo/dotfiles/emacs/.emacs.d/extra/ob-julia ++ + +
+Changing dir from julia code still works +
+cd("/") +realpath(".") ++
+/ ++ + +
+but is ephemeral (like fort the :dir
param)
+
realpath(".")
+
++/home/nixo/dotfiles/emacs/.emacs.d/extra/ob-julia ++ + +
+This is obtained by wrapping the src block in a cd()
call:
+
cd(folder) do + block +end ++
3.5 Error management
++If the block errors out, +
+ +x ++
ERROR | +UndefVarError(:x) | +
1 + "ciao"
+
+ERROR | +MethodError(+ | +(1 | +ciao) | +0x0000000000006426) | +
ERROR | +MethodError(+ | +(1 | +ciao) | +0x0000000000006420) | +
+It works in async +
+x ++
ERROR | +UndefVarError(:x) | +
+On external process (sync) +
+x ++
ERROR | +UndefVarError(:x) | +
+and on external process (async) +
+x ++
ERROR | +UndefVarError(:x) | +
+Error management can still be improved for helping with debug (see +scimax). +
+3.6 Using (:using
) and Import (:import
)
+
+To include dependencies, you can use :using
and :import
.
+
+Because of how the julia code is actually implemented, in order to use +specialized exports (e.g., DataFrames, see ) you need the +modules to be available before the block gets evaluated. The problem +can be solved in 2 (or 3 ways): +
+-
+
- Evaluating a block with using/import, then the other block +
- Using the header arguments +
- Fixing the Julia code :D +
+to use :import
, you need to pass arguments quoted:
+
+:using DataFrames Query :import "FileIO: load" "Plots: plot" ++
3.7 Results (:results output
, :results file
, )
++The default is to return the value: +
+ +10 ++
+10 ++ + +
+If results is output, it's included the stdout (what's printed in the +terminal). (This part still needs some work to be useful.) +
+ +10 ++
println(10) ++
+10 ++ + +
println("a") + +"10" + +println("b") ++
+a +b ++ + +
+Error (results output) +
+ +This will throw an error ++
+Base.Meta.ParseError("extra token \"will\" after end of expression") ++ + +
+Another error (result ouptut) +
+print(one(3,3)) ++
+MethodError(one, (3, 3), 0x0000000000006426) ++ + +
+A matrix +
+print(ones(3,3)) ++
+[1.0 1.0 1.0; 1.0 1.0 1.0; 1.0 1.0 1.0] ++
3.8 Supported Types
+
+Adding new types is easy (you need to define an orgshow()
function for
+your type. See init.jl). There's a simple mechanism that allows to
+define method on non-yet-existing types example.
+
+The current version supports a couple of useful type: DataFrames and +Plots. ob-julia needs community support to add more types: please help! +
+3.9 File output & Inlining
++There's native support for writing output to file. For unkown file +types, instead of inserting the output in the buffer it's written to the file. +
+ +zeros(3,3) ++
zeros(3,3) ++
Dict(10 => 10)
+
+
+Saving plots requires the Plots library. You can require it with the
+:using
header. There's the custom :size "tuple"
header argument for
+specifying the output size. It must be placed inside parentheses, and
+it's evaluated as julia object (that means it can contains variables
+and expressions).
+
plot(matrix) ++
+
+
+Matrix also has an automatic conversion (when Plots is loaded), so you
+don't even need to pass it to the plot()
function (there's a generic
+fallback that tries to plot anything saved to png or svg).
+
matrix ++
+
++Plots can also manage errors (in a visually-disturbing way). +
+ +this_is_undefined ++
+
+another_undefined_but_async ++
+
+DataFrame(x = 1:10, y = (0:9) .+ 'a')
+
+x | +y | +
---|---|
1 | +a | +
2 | +b | +
3 | +c | +
4 | +d | +
5 | +e | +
6 | +f | +
7 | +g | +
8 | +h | +
9 | +i | +
10 | +j | +
DataFrame(table) ++
data ++
x | +y | +
---|---|
1 | +a | +
2 | +b | +
3 | +c | +
4 | +d | +
5 | +e | +
6 | +f | +
7 | +g | +
8 | +h | +
9 | +i | +
10 | +j | +
3.9.1 Inlining (:inline no
, :inline
, :inline format
)
++Output to file and Inlining are different things but nicely fit +together to solve the same problem: inserting images or other objects +inside the buffer. +
+ +
+If your type can be represented inside the exported format (like
+images as svg/base-64 encoded pngs inside .html
, tex plots in a .tex
+file), the :inline
header is what you need. The behaviour changes
+based depending on your interactive use and on the desired output
+format.
+
+TODO: right now :results raw is required on export. How do we fix it? +
+ ++Examples: :inline keyword alone, in interactive evaluation, the output +inserted in the buffer is the usual. +
+matrix ++
+
1 | 2 | 3 | 4 |
---|---|---|---|
1 | 2 | 3 | 4 |
+But when you export the output to html, the output will be processed
+by julia, and inserted in the buffer (or a different representation
+for different export formats). This is not of much use with tables
+(even if you can customize the export, e.g. by passing the :width
+keyword), but is wonderful for pictures. If a result can be inserted
+in multiple ways (picture in html can be both inline png or svg), you
+can specify the desired format by passing the argument to the :inline
+keyword (like :inline svg
). In this case, the processed output is
+inserted also in interactive sessions.
+
matrix ++
+
1 | 2 | 3 | 4 |
---|---|---|---|
1 | 2 | 3 | 4 |
+Plots default to inline png +
+ +plot(matrix) ++
+ + + +
+ + +
+But you can also force svg (Since it's multiline, :wrap it with :wrap html
)
+
plot(matrix) ++
+ + + +
+4 Issues and Missing features
+-
+
- No automated tests yet +
- Not tested with remote host +
- Variable resolution of src-block is synchronous. If your :async src +block depends on another :async src block, the latter is evaluated +synchronously, then former asynchonously. This could be implemented +by using a simple queue, where next item is started in the +process-filter and where variables starting with julia-async: are +replaced. Right now I don't feel the need (if results are cached, +things already feels good). +
- For async evaluation to work the session must be started from +ob-julia (or you must register the filter function manually, +undocumented). +
:results output
is implemented but untested. I rarely find it +useful.
+- import/using errors are not reported +
5 Credits
++This project originally started as a fork of +astahlman/ob-async. However, because of changes in new version of Org +Mode, julia 1.0 release and unsupported features, I decided to start +over. +
+