org-babel-result: orchestrating over results you already have
Yesterday I argued that Org-Babel needs a small, documented
org-babel-call: a
way to call a named block from Lisp, pass it real values, and get a typed result
back. The companion came up almost immediately. Sometimes you don’t want to
call the block at all; you want the result it already produced.
The case is common. A producer block is slow, or hits a network or a database, or
has side effects you’d rather not repeat. Its #+RESULTS: are already in the
buffer, and when you feed it into a downstream block you want that stored value,
not another run of the producer.
Reading a stored result instead of computing one is half of what :cache yes
already does. The pieces are there, if undocumented:
org-babel-where-is-src-block-result finds a block’s #+RESULTS:, and
org-babel-read-result parses it into a Lisp value (the same two the cache path
calls). A “read, don’t execute” function is about five lines:
(defun org-babel-result (name)
"Return NAME's stored #+RESULTS from the buffer, without executing it."
(save-excursion
(goto-char (or (org-babel-find-named-block name)
(user-error "No source block named `%s'" name)))
(let ((pos (org-babel-where-is-src-block-result)))
(and pos (progn (goto-char pos) (org-babel-read-result))))))
The everyday use needs no orchestration at all, because a :var value that looks
like Lisp is evaluated. So you can drop the call straight into a header and bind a
recorded result to a variable:
#+begin_src emacs-lisp :var x=(org-babel-result "expensive")
(* 2 x)
#+end_src
Run that block and x is the stored value. Maybe you forgot to add :cache yes;
maybe you only decided afterward that you wanted to process the result further.
Either way you can now, without re-running the producer, and tables come through
as lists, like any :var.
It also composes with org-babel-call (the proposed function from yesterday’s
post), so a stored result can be one input to a larger call, with no execution:
(org-babel-call "process" :data (org-babel-result "expensive-fetch"))
This is deliberately not caching. :cache yes is caching: it writes a checksum of
the inputs next to the result and reuses the result only while that checksum still
matches, which means it runs the block once to establish the hash.
org-babel-result does the opposite. It never runs the block and never checks
anything, and hands you exactly what is in the buffer. Two consequences:
- It can be stale. If the producer’s inputs changed since its
#+RESULTS:were written, you get the old value silently; there is no hash to catch it. You are asserting that the recorded result is the one you want. If you need the guarantee, use:cache yesand accept the rerun. - You get the rendered form.
org-babel-read-resultreturns whatever the#+RESULTS:block holds, re-parsed: a table becomes a list, a scalar a string or number, anoutputblock its text. That can be flatter than the live return value. The buffer holds the displayed result, not the original object.
Both follow from what the function is for: it reads what’s written down. That
suits reproducible documents, where the results are committed to the .org file
alongside the code. You work from the recorded outputs, with no recomputation and
no side effects.
org-babel-call runs a named block to get its value; org-babel-result reuses
the one already recorded. Two small functions built on machinery Org already has:
compute a named block’s result, or take what’s written down.