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