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 yes and accept the rerun.
  • You get the rendered form. org-babel-read-result returns whatever the #+RESULTS: block holds, re-parsed: a table becomes a list, a scalar a string or number, an output block 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.