Elixir and Distel
Distel: Emacs talking Erlixir
alchemist.el is a Swiss army knife for working with Elixir from the comfort of Emacs. It can run mix tasks, look up Elixir source documentation, autocomplete, ... etc. Alchemist is awesome.
But, here’s the rub: often alchemist has to boot an Erlang VM to evaluate a single expression. Running tests boots a VM, looking up a function’s source doc boots a VM, each prefix autocompletion boots a VM. This is slow, and if you want the VM to load the code you’re actively developing you better not have any compile-time errors.
Plus, with features like hot code reloading, supervision trees, and remote shells, Erlang loves to be manipulated live. Stuart Sierra, a Clojurian, said:
One of the great pleasures of working with a dynamic language is being able to build a system while simultaneously interacting with it.
Enter distel. Distel implements the Erlang distribution protocol in ELisp. More on the nitty gritty of that later, but long story short: distel allows Emacs to pass as an Erlang node; a gnu in Erlang’s clothing (?!). Instead of booting a new Erlang VM to evaluate an expression, the functions can be called on a running node via RPC. Start your application on the remote node with an autoreloader and you have a long-lived development session.
The rest of this post is a brief overview of how to use distel from an Elixir perspective.
I’ll leave the installation up to you, clever reader.
For programming with distel, the Emacs info pages are surprisingly mediocre. Instead, see the included Gorrie 02.
First, you need a node running distributed Erlang; booting an Erlang VM running an Elixir console will do:
iex --sname emacs [-S mix]
The first time you run any distel command you’ll be prompted for
the name of the Erlang node to connect to. If you used the short
emacs as above, the node name is just
If you’re having issues, make sure that your cookie matches.
RPC against an Erlang node
Distel loads an RPC library, rex, into the remote node. On the
Emacs side, distel provides
erl-send-rpc as a helper to call into
rex. The function signature,
(erl-send-rpc NODE MODULE FUNCTION
ARGS), is reminiscient to Erlang RPC and apply calls and works
After handling the call, the target node sends a message
back to Emacs wrapped in an
rex tuple, which can be caught with
erl-receive. Look at the function docs for
erl-receive -- between
pattern matching and variable binding it does quite a bit.
Putting it all together, here’s a function that will call
Enum.count/1 with the arg
LIST on the remote node:
(defun elixir-enum-count (node list) "Use NODE to call `Enum.count(LIST)'" (erl-spawn (erl-send-rpc node 'Elixir.Enum 'count (list expr)) (erl-receive () ((['rex results] (message "results: %S" results))))))
Notice that the module,
Enum is referenced using its absolute
Elixir.Enum. You must use the absolute name when
referencing an Elixir module.
Another catch: the result of
erl-receive isn’t returned to the
calling context; you must use the continuation passing-style to
handle results. i.e. callbacks, ahoy! Here’s the previous example
rewritten with the result being passed to the callback function.
(defun proximel-enum-count (node expr &optional callback) "Use NODE to generate a list of completions for EXPR. Optionally call CALLBACK with the completions." (erl-spawn (erl-send-rpc node 'Elixir.Proximel 'expand (list expr)) (erl-receive (callback) ((['rex results] (progn (message "results: %S" results) (if callback (funcall callback results))))))))
All iterations of
proximel-enum-count accept the Erlang
the first argument. The current value of
NODE can be fetched with
erl-target-node. This makes it easy to define
interactive functions that accept a node:
(defun elixir-enum-count (node list) "Use NODE to call `Enum.count(LIST)'" (interactive (list (erl-target-node) (read-minbuffer "Enter list: "))) (erl-spawn (erl-send-rpc node 'Elixir.Enum 'count (list expr)) (erl-receive () ((['rex results] (message "results: %S" results))))))
I put together a basic repo, proximel, which demos loading beam files compiled from Elixir source into a remote node, and basic Elixir autocompletion.
In a future post I’ll dissect Elixir autocompletion over distel with company-mode.