Publish a post about Valeri+org-babel
This commit is contained in:
parent
30f6f5e030
commit
790a749cf3
1 changed files with 76 additions and 0 deletions
|
@ -0,0 +1,76 @@
|
||||||
|
X-Date: 2024-10-12T20:12:24Z
|
||||||
|
X-Note-Id: 087d89cd-27ec-43d4-9d46-b48eed3ce301
|
||||||
|
Subject: Valeri notebook programming with Org Babel
|
||||||
|
X-Slug: valeri_notebook_programming_with_org_babel
|
||||||
|
|
||||||
|
I really wanted to get an interactive programming environment for [Valeri](https://git.knazarov.com/knazarov/valeri)
|
||||||
|
which works in a similar fashion to [Jupyter notebooks](https://jupyter.org/). It is of course possible to add the
|
||||||
|
support for the Valeri kernel to Jupyter itself, but it's a lot of work. So I opted for
|
||||||
|
[Org Babel](https://orgmode.org/worg/org-contrib/babel/intro.html), which is an extension to Emacs
|
||||||
|
[org-mode](https://orgmode.org/) allowing the execution of code snippets within the Org markup code blocks.
|
||||||
|
|
||||||
|
The [implementation](https://git.knazarov.com/knazarov/valeri/src/branch/master/emacs) turned out to be quite
|
||||||
|
simple: in just a few hundred lines of code, I've got support for running the Valeri REPL as a background
|
||||||
|
"inferior" process in Emacs. The rest just involved writing some glue code to add a bridge from Babel to
|
||||||
|
send the code body to the REPL and getting the text results back. Most of the code handling the interation
|
||||||
|
with the inferior REPL is already in Emacs base, so you don't have to write it on your own.
|
||||||
|
|
||||||
|
If you exclude all the glue code, this is what the implementation boils down to:
|
||||||
|
|
||||||
|
```
|
||||||
|
(defun org-babel-valeri-evaluate (buffer body)
|
||||||
|
"Pass BODY to the Valeri process in BUFFER."
|
||||||
|
(let ((escaped-body (format "\x1b[200~%s\x1b[201~" body))
|
||||||
|
(eoe-string (format "\n(println \"%s\")\n" valeri-eoe-indicator)))
|
||||||
|
(mapconcat
|
||||||
|
#'identity
|
||||||
|
(butlast
|
||||||
|
(split-string
|
||||||
|
(mapconcat
|
||||||
|
#'org-trim
|
||||||
|
(org-babel-comint-with-output
|
||||||
|
(buffer valeri-eoe-indicator t escaped-body)
|
||||||
|
(insert (org-babel-chomp escaped-body) eoe-string)
|
||||||
|
(comint-send-input nil t))
|
||||||
|
"\n") "[\r\n]")) "\n")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
It just takes the `body` string, wraps it into the [bracketed paste](https://en.wikipedia.org/wiki/Bracketed-paste)
|
||||||
|
escape sequence, sends to the REPL buffer, reads the result back and trims unnecessary symbols.
|
||||||
|
|
||||||
|
The usage of this integration is quite simple as well. You just create a file with `.org` extension that can look
|
||||||
|
like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
* Notebook programming with Valeri
|
||||||
|
|
||||||
|
The following code block will create a new persistent
|
||||||
|
interpreter session and load the factorial function
|
||||||
|
there.
|
||||||
|
|
||||||
|
#+begin_src valeri :session val
|
||||||
|
(fn fact (n)
|
||||||
|
(if (<= n 0)
|
||||||
|
1
|
||||||
|
(* n (fact (- n 1)))))
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
: #<function fact>
|
||||||
|
|
||||||
|
And this code block will run the factorial function
|
||||||
|
and output the result.
|
||||||
|
|
||||||
|
#+begin_src valeri :session val
|
||||||
|
(println "12! is" (fact 12))
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
: 12! is 479001600
|
||||||
|
```
|
||||||
|
|
||||||
|
If you position the cursor in the `begin_src/end_src` block and press Ctrl-C Ctrl-C, it will be evaluated
|
||||||
|
and the `#+RESULTS` will be updated. What is also convenient is that multiple code blocks can share a
|
||||||
|
session, so you can define a function in the first block and call it in the subsequent block.
|
Loading…
Reference in a new issue