10

I want to understand how org-babel's Library Of Babel works. Seems like a powerful yet underused tool.

The documentation says that I can

add code to the library, by first saving the code in regular ‘src’ code blocks of an Org file, and then load the Org file with org-babel-lob-ingest, which is bound to C-c C-v i.

What is that org-babel-lob-ingest truly doing? It is just appending all source blocks within an Org File to another file?

And what are the use cases of this? Can I see what I have in my Library interactively? Can I use noweb syntax with source blocks within the Library? What do I need to do to start using it?

Any examples and links to tutorials are very welcome.

Daniel
  • 3,563
  • 16
  • 41
  • It is appending the list of named source blocks it finds in the file to the variable `org-babel-library-of-babel`. When looking for source blocks, org-babel looks in the current file as well as the list stored in the above variable. The answer by @mutbuerger describes the details very well I think. – NickD Apr 04 '18 at 18:30

2 Answers2

10

There is a nice introduction to the library of babel in library-of-babel.org which is located in Org’s source directory. To use those examples of named source-code blocks in other files, populate the org-babel-library-of-babel variable with

#+begin_src elisp :results scalar
(org-babel-lob-ingest "/path/to/org-mode/doc/library-of-babel.org")
#+end_src

#+results:
: 21

One of the 21 blocks is named “transpose” and probably does what it’s supposed to do:

#+name: tbl
| a | 1 |
| d | 2 |
| a | 3 |
| d | 4 |
| d | 5 |
| c | 6 |

#+begin_src elisp :results table :post transpose(table=*this*) :var var=tbl
var
#+end_src

#+results:
| a | d | a | d | d | c |
| 1 | 2 | 3 | 4 | 5 | 6 |

You may as well add your own code blocks, especially something you may find yourself using a lot in the future. As an example, the following allows me to aggregate values in the named table by the first column:

#+name: aggregatebycol1
#+begin_src elisp :results table :var table='() fun='()
(let (res)
  (mapc
   (lambda (x)
     (push `(,(car x) ,(apply fun (mapcar 'cadr (cdr x)))) res))
   (seq-group-by 'car table))
  (nreverse res))
#+end_src

Save the block in any file and add it to org-babel-library-of-babel:

#+begin_src elisp :results scalar
(org-babel-lob-ingest (buffer-file-name))
#+end_src

#+results:
: 1

#+header: :post aggregatebycol1(table=*this*, fun='+)
#+begin_src elisp :results table :var var=tbl
var
#+end_src

#+results:
| a |  4 |
| d | 11 |
| c |  6 |
mutbuerger
  • 3,434
  • 14
  • 22
6

TL;DR: Using a persistent library of babel stored in one file can be a simple 3-step setup:

  • Create an org-mode file ~/.emacs.d/library-of-babel.org.
  • Add a line (org-babel-lob-ingest "~/.emacs.d/library-of-babel.org") to your Emacs conf.
  • Collect useful functions in that file, they will be read during emacs startup.

The Library-Of-Babel-file is where e.g. the aggregatebycol1 block from @mutbuerger would be saved to.

Another simple example use-case would be having a code-block, that generates table data with a header row, but does not mark the headerrow with an 'hline. This is not tragic for simple display, but may make further automated processing more involved. The solution here could be using a small code-block for post-processing from somewhere on the internet:

#+name: addhdr
#+begin_src emacs-lisp :var tbl=""
(cons (car tbl) (cons 'hline (cdr tbl)))
#+end_src

This will simply pipe through the data while splicing in an 'hline as a second row.

To use this block later in other org files, simply add a :post-processing stanza to your data-generating org source block:

#+NAME: Example
#+BEGIN_SRC elisp :post addhdr(*this*)
'(("Header1" "Column2" "Three")("R1C1V" "2" "C3R1")("4" "5" "6"))
#+END_SRC

#+RESULTS: Example
| Header1 | Column2 | Three |
|---------+---------+-------|
| R1C1V   |       2 | C3R1  |
| 4       |       5 | 6     |

You can also easily give pre-existing tables to functions in your LOB:

#+NAME: ExData
| h1    | h2    |
| dh1r1 | dh2r1 |
| dh1r2 | dh2r2 |

#+CALL: addhdr(ExData)

In my library I have chapters to organize different types of functionality: Data-Generation, Filtering, PrettyPrinting, ... Just remember to ingest again after adding new blocks.

Alex Stragies
  • 403
  • 3
  • 14
  • 1
    Great answer. Thanks for sharing. Would you have a link to your library-of-babel.org file? – Daniel Jan 30 '20 at 18:32