8.9 KiB
Examples of usage of dds
Introduction
This document shows some examples of usage of the modules in dds
with Org-mode. It relies on emacs-ob-racket.
The following section describes how Org-mode can interact with
Racket, and how this interaction can be used for a fluid workflow
with dds
. In particular, the code block munch-table
is defined
in this section.
The subsequent sections show off some the functionalities of the
submodules of dds
.
Org-mode, Racket, and dds
<<intro>>
Importing a module from file
To require the modules from the files of dds
, you can use the
following code (I only reset the prelude here because I set at the
top of this file):
#lang racket
(require (file "~/Candies/prj/racket/dds/networks.rkt"))
(require (file "~/Candies/prj/racket/dds/utils.rkt"))
Note that this code will not work with :results value
. I think it
is because in this case the code is not really evaluated at top
level.
These initialisation lines can be put into the prologue of every
code block in a subtree by setting :prologue
via
:header-args:racket:
in the properties drawer. Check out the
properties drawer of this section for an example.
Alternatively, this property can be set via a #+PROPERTY
line at
the top the file. For example, this file has such a line. Whenever
this property line changes, refresh the setup of the file by hitting
C-c C-c
on the property line. This will update the prologue for
all racket code blocks.
Finally, you can also set :prologue
(and other properties with
long values) in the following way:
(st '((a . 1)))
'#hash((a . 1))
Output formats for results of evaluation of code blocks
This section of the Org manual describes various different formats
for presenting the results of code blocks. I find the following
three particularly useful as of [2020-02-22 Sat]: output
, list
,
and table
.
The output
result format is the simplest and the most natural
ones. It works as if the code block were inserted into a module
which would then be evaluated.
(println "This is the first line of output.")
(println (+ 1 2))
(println "This the third line of output.")
"This is the first line of output." 3 "This the third line of output."
The list
result format typesets the result of the last line in the
code block as a list:
'(1 "hello" (and x y))
- 1
- "hello"
- (and x y)
Note how nested lists are not recursively shown as nested Org-mode lists.
For some reason, the list
output format does not work with the
result drawer:
'(1 "hello" (and x y))
- (1 "\"hello\"" (and x y))
Finally, the table
result format typesets the output as a table:
'((a . #t) (b . #f))
a | #t |
b | #f |
This is clearly very useful for printing states (and hash tables, more generally):
(st '((a . 1) (b . #f) (c . "hello")))
a | 1 |
b | #f |
c | "hello" |
A note about printing update function forms
Automatic table typesetting may go in the way of readability for hash tables whose values are lists, as the following example shows:
#hash((a . (and a b)) (b . (not b)))
a | and | a | b |
b | not | b |
To tackle this issue, dds/utils
provides
stringify-variable-mapping
(with the shortcut sgfy
) which
converts all the values of a given variable mapping to strings:
(sgfy #hash((a . (and a b)) (b . (not b))))
a | "(and a b)" |
b | "(not b)" |
Reading Org-mode tables<<tabread>>
Org-mode allows supplying tables as arguments for code blocks.
a | "(and a b)" |
b | (or b (not a)) |
tab
((a (and a b)) (b (or b (not a))))
Unfortunately, the same trick does not work with Racket directly, because Racket interprets the first elements in the parentheses as function applications:
tab
application: not a procedure; expected a procedure that can be applied to arguments given: "a" arguments…: "(and a b)" context…: "/tmp/babel-qkvrRR/org-babel-c4wuju.rkt": [running body] temp37_0 for-loop run-module-instance!125 perform-require!78
Fortunately, we can easily remedy this problem by creating a named parameterised Elisp source block which will explicitly convert the table to a string:
(prin1 tab)
(("a" "(and a b)") ("b" "(or b (not a))"))
We can now correctly receive this table in a Racket source code
block by threading it through munch-table
:
(println tab)
"((\"a\" \"(and a b)\") (\"b\" \"(or b (not a))\"))"
dds/utils
has several functions for parsing such strings, and
notably read-org-variable-mapping
, with the shortcut unorg
:
(unorg tab)
'#hash((a . (and a b)) (b . (or b (not a))))
Of course, we can use munch-table
to prepare any other table than
test-table
for use with Racket:
a | (not a) |
b | (and a c) |
c | (and a (not b)) |
(unorg tab)
'#hash((a . (not a)) (b . (and a c)) (c . (and a (not b))))
Inline graph visualisation with Graphviz
Some functions in dds
build graphs:
(build-interaction-graph (unorg bf))
#<unweighted-graph>
The graph
library allows building a Graphviz description of the
constructed graph. (Note that you have to install the graph
library by running raco pkg install graph
and require it. The
long property line at the top of this file defining the prologue for
racket source code blocks takes care of requiring graph
.)
(display (graphviz (build-interaction-graph (unorg bf))))
digraph G { node0 [label="c"]; node1 [label="b"]; node2 [label="a"]; subgraph U { edge [dir=none]; node0 -> node1; node2 -> node2; } subgraph D { node2 -> node0; node2 -> node1; } }
You can have an inline drawing of this graph by calling the previous
code block (igraph
) via a noweb reference in Graphviz/DOT source
block:
<<igraph()>>
Note that the graph
library draws self-loops as undirected edges.
It also draws double-sided edges as undirected edges (e.g., in the
preceding graph, b depends on c and c depends on b).