Babel: Language Compatability
Overview
This page looks at how consistent various languages are in babel. The goal is to document inconsistencies so they can be resolved or at least better understood.
Support for Common Headers
language | :results value | :results output | default :results | :prologue / :epilogue |
---|---|---|---|---|
C | no | yes | output | yes |
java | yes | yes | output | no |
python | yes | yes | value | yes |
elisp | yes | yes | value | yes |
shell | yes | yes | output | yes |
Default Behaviors
language | write source files to |
---|---|
C | tempdir |
java | :dir else tempdir |
python | tempdir |
elisp | no tempfiles |
shell | no tempfiles |
Variables
Summary
language | int | double | string | singleton list | int list | double list | string list | int table | double table | string table |
---|---|---|---|---|---|---|---|---|---|---|
C | error | error | error | error | 2d string list | 2d string list | 2d list | expected | expected | expected |
java | error | error | error | error | 2d string list | 2d string list | 2d list | expected | expected | expected |
python | variant1 | variant1 | variant1 | variant1 | 2d string list | 2d string list | 2d list | expected | expected | expected |
elisp | variant1 | variant1 | variant1 | error | 2d string list | 2d string list | 2d list | expected | expected | expected |
shell | variant1 | variant1 | variant1 | expected | list of strings | n/a | expected | n/a | n/a | n/a |
For tables, values are passed to code blocks in two dimensional arrays
or lists with item types (ints, doubles, or strings) that match the
data. Lists are passed as two dimensional arrays as if they are
tables with one column of data, and all values are passed as strings.
Only shell
passes a list as a single dimentional array.
More details on how this can cause confusion in Round Trip.
Shell doesn't support doubles or multidimensional arrays, so those cases don't apply.
Callouts
- should ints and doubles be passed as strings?
- should lists be passed as 2d lists tables?
- should
:results output
requireraw
to produce an org list? - should items include the trailing endline?
- why aren't singleton lists passed as lists?
Single Item
int
The following examples use this data:
#+name: int-item-data 10
Each example just outputs the given number surrounded in single quotes. This is the expected response:
#+RESULTS: : '10'
Some languages return this instead:
#+RESULTS: : '10 : '
- C
#+begin_src C :results output :var item=int-item-data printf("'%s'", item); #+end_src
This does not compile because the variable value includes the trailing endline and C doesn't allow multiline string literals.
- java
#+begin_src java :results output :var item=int-item-data System.out.println(String.format("'%s'", item)); #+end_src
This does not compile because the variable value includes the trailing endline and java doesn't allow multiline string literals.
- python
#+begin_src python :results output :var item=int-item-data print("'{}'".format(item)) #+end_src
- elisp
#+begin_src elisp :results output :var item=int-item-data (princ (format "'%s'" item)) #+end_src
- shell
#+begin_src sh :results output :var item=int-item-data echo "'$item'" #+end_src
double
The following examples use this data:
#+name: double-item-data 10.1
Each example just outputs the given number surrounded in single quotes. This is the expected response:
#+RESULTS: : '10.1'
Some languages return this instead:
#+RESULTS: : '10.1 : '
- C
#+begin_src C :results output :var item=double-item-data printf("'%s'", item); #+end_src
This does not compile because the variable value includes the trailing endline and C doesn't allow multiline string literals.
- java
#+begin_src java :results output :var item=double-item-data System.out.println(String.format("'%s'", item)); #+end_src
This does not compile because the variable value includes the trailing endline and java doesn't allow multiline string literals.
- python
#+begin_src python :results output :var item=double-item-data print("'{}'".format(item)) #+end_src
- elisp
#+begin_src elisp :results output :var item=double-item-data (princ (format "'%s'" item)) #+end_src
- shell
#+begin_src sh :results output :var item=double-item-data echo "'$item'" #+end_src
string
The following examples use this data:
#+name: string-item-data ten
Each example just outputs the given number surrounded in single quotes. This is the expected response:
#+RESULTS: : 'ten'
Some languages return this instead:
#+RESULTS: : 'ten : '
- C
#+begin_src C :results output :var item=string-item-data printf("'%s'", item); #+end_src
This does not compile because the variable value includes the trailing endline and C doesn't allow multiline string literals.
- java
#+begin_src java :results output :var item=string-item-data System.out.println(String.format("'%s'", item)); #+end_src
This does not compile because the variable value includes the trailing endline and java doesn't allow multiline string literals.
- python
#+begin_src python :results output :var item=string-item-data print("'{}'".format(item)) #+end_src
- elisp
#+begin_src elisp :results output :var item=string-item-data (princ (format "'%s'" item)) #+end_src
- shell
#+begin_src sh :results output :var item=string-item-data echo "'$item'" #+end_src
List
List of ints
The following examples use this data:
#+name: int-list-data - 1 - 2 - 3
All examples compute the sum of the numbers in the list. Output should look like:
#+RESULTS: : 6
- C
#+begin_src C :results output :var items=int-list-data int sum = 0; for (int ii=0; ii<items_rows; ii++) { sum += atoi(items[ii][0]); } printf("%d", sum); #+end_src
- java
#+begin_src java :results value :var items=int-list-data import java.util.stream.Collectors; return items.stream() .collect(Collectors.summingInt(x -> Integer.parseInt(x.get(0)))); #+end_src
- python
#+begin_src python :var items=int-list-data return sum([int(x[0]) for x in items]) #+end_src
- elisp
#+begin_src elisp :var items=int-list-data (apply '+ (mapcar (lambda (x) (string-to-number (car x))) items)) #+end_src
- shell
#+begin_src sh :var items=int-list-data sum=0 for item in $items; do sum=$(($sum + $item)) done echo $sum #+end_src
List of doubles
The following examples use this data
#+name: double-list-data - 1.1 - 2.2 - 3.3
All examples compute the sum of the numbers in the list. Output should look like:
#+RESULTS: : 6.6
- C
#+begin_src C :var items=double-list-data :includes <stdlib.h> double sum = 0; for (int ii=0; ii<items_rows; ii++) { sum += atof(items[ii][0]); } printf("%lf", sum); #+end_src
- java
#+begin_src java :results value :var items=double-list-data import java.util.stream.Collectors; return items.stream() .collect(Collectors.summingDouble(x -> Double.parseDouble(x.get(0)))); #+end_src
- python
#+begin_src python :var items=double-list-data return sum([float(x[0]) for x in items]) #+end_src
- elisp
#+begin_src elisp :var items=double-list-data (apply '+ (mapcar (lambda (x) (string-to-number (car x))) items)) #+end_src
- shell
Shell doesn't support doubles.
List of strings
The following examples use this data:
#+name: string-list-data - a - b - c
Each example conncatenates the input into a space delimited list. Output looks like:
#+RESULTS: : a b c
- C
#+begin_src C :results output :var items=string-list-data :include <string.h> char ret[8]; memset(ret, 0, 8); for (int ii=0; ii<items_rows; ii++) { strcat(ret, " "); strcat(ret, items[ii][0]); } printf("%s", ret); #+end_src
- java
#+begin_src java :results value :var items=string-list-data import java.util.stream.Collectors; return items.stream() .map(x -> x.get(0)) .collect(Collectors.joining(" ")); #+end_src
- python
#+begin_src python :var items=string-list-data return " ".join([x[0] for x in items]) #+end_src
- elisp
#+begin_src elisp :var items=string-list-data (mapconcat #'car items " ") #+end_src
- shell
#+begin_src sh :var items=string-list-data ret="" for item in $items; do ret="$ret $item" done echo $ret #+end_src
Singleton List
There is inconsistent behavior between lists of one vs many items. See Trimming a List to One Item for details.
The following examples use this data:
#+name: single-list-data - one
Each source block just iterates over the input list, printing each value. Expected output is:
#+RESULTS: : one
Some languages give this result:
#+RESULTS: : o : n : e
- C
#+begin_src C :results output :var items=single-list-data[,0] :include <string.h> for (int ii=0; ii<items_cols; ii++) { printf("%s\n", items[ii]); } #+end_src
This doesn't compile since
items
is passed as achar*
instead ofchar*[]
. - java
#+begin_src java :var items=single-list-data[,0] for (String item : items) System.out.println(item); #+end_src
This doesn't compile since
items
is passed as aString
instead ofString[]
. - python
#+begin_src python :results output :var items=single-list-data[,0] for item in items: print(item) #+end_src
- elisp
#+begin_src elisp :results output :var items=single-list-data[,0] (dolist (item items) (princ (format "%s\n" item))) #+end_src
This fails with a type error because
items
is passed as a string instead of a list. - shell
#+begin_src sh :var items=single-list-data[,0] for item in $items; do echo $item done #+end_src
Table
Table of ints
The following source blocks operate on this table:
#+name: int-table-data | 1 | 2 | | 3 | 4 |
Each source block sums the values found in the table. The output show look like:
#+RESULTS: : 10
- C
#+begin_src C :var items=int-table-data int sum = 0; for (int ii=0; ii<items_rows; ii++) { for (int jj=0; jj<items_cols; jj++) { sum += items[ii][jj]; } } printf("%d", sum); #+end_src
- java
#+begin_src java :results value :var items=int-table-data int sum = 0; for (List<Integer> row : items) { for (Integer col : row) { sum += col; } } return sum; #+end_src
- python
#+begin_src python :var items=int-table-data sum = 0 for row in items: for col in row: sum += col return sum #+end_src
- elisp
#+begin_src elisp :var items=int-table-data (apply '+ (mapcar (lambda (x) (apply '+ x)) items)) #+end_src
- shell
The table becomes an associated list instead of a 2d array. Bash doesn't support multidimensional arrays.
Table of doubles
The following source blocks operate on this table:
#+name: double-table-data | 1.1 | 2.3 | | 3.1 | 4.3 |
Each source block sums the values found in the table. The output show look like:
#+RESULTS: : 10.8
- C
#+begin_src C :var items=double-table-data double sum = 0; for (int ii=0; ii<items_rows; ii++) { for (int jj=0; jj<items_cols; jj++) { sum += items[ii][jj]; } } printf("%lf", sum); #+end_src
- java
#+begin_src java :results value :var items=double-table-data double sum = 0; for (List<Double> row : items) { for (Double col : row) { sum += col; } } return sum; #+end_src
- python
#+begin_src python :var items=double-table-data sum = 0 for row in items: for col in row: sum += col return sum #+end_src
- elisp
#+begin_src elisp :var items=double-table-data (apply '+ (mapcar (lambda (x) (apply '+ x)) items)) #+end_src
- shell
The table becomes an associated list instead of a 2d array. Bash doesn't support multidimensional arrays.
Table of strings
The following source blocks operate on this table:
#+name: string-table-data | a | b | | c | d |
concatenates the strings found in the table. The output show look like:
#+RESULTS: : a b c d
- C
#+begin_src C :results output :var items=string-table-data :includes <string.h> char ret[8]; memset(ret, 0, 8); for (int ii=0; ii<items_rows; ii++) { for (int jj=0; jj<items_cols; jj++) { strcat(ret, " "); strcat(ret, items[ii][jj]); } } printf("%s", ret); #+end_src
- java
#+begin_src java :results value :var items=string-table-data import java.util.stream.Collectors; return items.stream() .map(x -> String.join(" ", x)) .collect(Collectors.joining(" ")); #+end_src
- python
#+begin_src python :var items=string-table-data return " ".join([" ".join(x) for x in items]) #+end_src
- elisp
#+begin_src elisp :var items=string-table-data (mapconcat (lambda (x) (mapconcat #'identity x " ")) items " ") #+end_src
- shell
The table becomes an associated list instead of a 2d array. Bash doesn't support multidimensional arrays.
Results
Summary
language | return list | output list | return table | output table |
---|---|---|---|---|
C | no support | expected (w/ raw) | no support | expected |
java | expected | expected (w/ raw) | expected | variant1 |
python | expected | expected (w/ raw) | expected | variant1 |
elisp | expected | expected (w/ raw) | expected | variant1 |
shell | expected | expected (w/ raw) | expected | expected |
There is consistent behavior across languages for :results value
but
there are some inconsistencies with :results output
.
My expectation is that writing rows of comma separated values should
result in a table, but in some cases the :results raw
is required
for this to work and in other cases that is not enough.
Callouts
- Can C support
:results value
? - should
:results output
requireraw
and write vertical bars to produce an org table?
List
When we return a list from a source code block, we want it to look like an org list.
#+RESULTS: - one - two
:results value
The following examples use :results value list
.
:results output
The following examples use :results output raw list
. These have to
use raw
in order to work.
- C
#+begin_src C :results output raw list printf("one\n"); printf("two\n"); #+end_src
- java
#+begin_src java :results output raw list System.out.println("one"); System.out.println("two"); #+end_src
- python
#+begin_src python :python python3 :results output raw list print("one") print("two") #+end_src
- elisp
#+begin_src elisp :results output raw list (princ "one\n") (princ "two") #+end_src
- shell
#+begin_src sh :results output raw list echo "one" echo "two" #+end_src
Table
When we return a table from a source code block, we want it to look like an org table.
#+RESULTS: | one | two | | three | four |
Some languages return this instead.
#+RESULTS: : one, two : three, four
:results value
The following examples use :results value table
.
- C
C has no support for
:results value
. - java
#+begin_src java :results value table String [][] ret = {{"one","two"}, {"three", "four"}}; return ret; #+end_src
- python
#+begin_src python :python python3 :results value table return (("one", "two"), ("three", "four")) #+end_src
- elisp
#+begin_src elisp :results value table '(("one" "two") ("three" "four")) #+end_src
:results output
The following examples use :results output table
.
- C
#+begin_src C :results output table printf("one, two\n"); printf("three, four\n"); #+end_src
- java
#+begin_src java :results output table System.out.println("one, two"); System.out.println("three, four"); #+end_src
that fails but this "raw table" output works:
#+begin_src java :results output raw table System.out.println("|one| two"); System.out.println("|three| four"); #+end_src
- python
#+begin_src python :python python3 :results output table print("one, two") print("three, four") #+end_src
doesn't work but raw table works
- elisp
#+begin_src elisp :results output table (princ "one, two\n") (princ "three, four") #+end_src
doesn't work but raw table works
- shell
#+begin_src sh :results output table echo "one, two\nthree, four" #+end_src
Round Trip Between Source Blocks
If a source block (ret-list-source
) returns a single dimensional
array or list, it becomes an org list (ret-list-result
).
#+name: ret-list-source #+begin_src python :results list return [1,2,3] #+end_src
#+name: ret-list-result #+RESULTS: ret-list-source - 1 - 2 - 3
Then if another source block (read-list-result
) accepts that list
from the org buffer, it becomes a two dimensional table with one
column.
#+name: read-list-result #+begin_src python :var a=ret-list-result :results list return a #+end_src
#+RESULTS: read-list-result - ("1") - ("2") - ("3")
But if a source block accepts the output directly from the
ret-list-source
, the input will be a single dimensional array.
#+name: read-list-direct #+begin_src python :var a=ret-list-source :results list return a #+end_src
#+RESULTS: read-list-direct - 1 - 2 - 3
Trimming a List to One Item
If a list contains more than one item, as in two-list-data
, source
blocks can access its items as a single dimensional list by indexing.
Given the following data and source block:
#+name: two-list-data - one - two
#+begin_src python :results output :var items=one-list-data[,0] for item in items: print (item) #+end_src
The result, as expected, is:
#+RESULTS: : one : two
But if the list only contains one item, as in one-list-data
, it is
no longer passed to the source block as a list.
#+name: one-list-data - one
Execution of the same source block as used above, but pointing at
one-list-data
, results in:
#+RESULTS: : o : n : e
This can cause a problem if you trim your data to a single item in order to test a source block. Suddenly the source block is accepting a different data type.