Intro to ob-elixir, Written with ob-elixir

Victor Olinasc’s ob-elixir does what it says on the tin: Org Mode Elixir language support. Also known as Elixir literate programming using Emacs.

The implementation is still a ways from parity with something like ob-python, but let’s kick the tires and see where it’s at. This post will run through examples showing some of the features ob-elixir currently supports. All of the code snippets were run from within Emacs using ob-elixir.

Org-Babel + Elixir

The best place to get started is with the documentation, but here is what an Elixir code block should resemble:

  #+begin_src elixir
    IO.inspect :result


ob-elixir writes your code block to a .exs file and then evaluates the file using the elixir executable. The first line of standard output is passed off to Org Mode, which then does its best to transform it into a table.

A one dimensional list or tuple is transformed into a table row:

    # it's as easy as
    [42, :foo, "bar"]
      |> IO.inspect
Table 1: Values
42 :foo bar

Each row in a two dimensional list becomes a row in a table:

    [1, 2, 3]
      |> Enum.map(&({&1, &1*&1}))
      |> IO.inspect
Table 2: Squares
1 1
2 4
3 9

Finally, a potentially useful example of getting the top 5 processes sorted by heap size. It also prints the total heap size and number of reductions:

    output_keys = [:registered_name, :heap_size, :total_heap_size, :reductions]
      |> Enum.map(&Process.info/1)
      |> Enum.filter(&Dict.has_key?(&1, :registered_name))
      |> Enum.sort(&(&1[:heap_size] > &2[:heap_size]))
      |> Enum.map(&(for key <- output_keys, do: &1[key]))
      |> Enum.take(5)
      |> IO.inspect
Table 3: Top 5 processes by heap size
:application_controller 28690 75112 53231
:kernel_sup 6772 35462 48964
:code_server 4185 32875 176097
:erl_prim_loader 4185 8370 1142203
:init 2586 3573 12554
Parsing Issues

The parser currently uses python style syntax parsing, and doesn’t support Elixir reader expressions within lists, i.e. values prefixed with #, like #PID<0.0.0>:

    # Works
    IO.inspect self
    # Raises an error -- `Invalid read syntax: "#"`
    IO.inspect [self]

I’m sure there are additional parsing issues -- report them if you find them!


You can pass variables into a code block:

Table 4: Some mammals
Common Name Order
tiger carnivora
orangutan primate
armadillo cingulata
brown bear carnivora
capuchin primate
      |> Enum.filter(fn ([_, "primate"]) -> true; _ -> false end)
      |> IO.inspect
Table 5: Some primates
orangutan primate
capuchin primate

Future work?

  • Persistent sessions -- could be backed by emacs comint or distel style distributed erlang.
  • Avoid having to call IO.inspect to return a value.
  • Parsing reader values
  • Optionally cleanup .exs file

Written by