Posts tagged erlang

Erlang Alignment in Emacs

Just realized that the default align function in Emacs is quite capable. To indent, for example, a list of tuples with data, add the following alignment rule to your .emacs-file:

(add-hook 'align-load-hook
          (lambda ()
            (add-to-list 'align-rules-list
                         '(erlang-align
                           (regexp . ",\\(\\s-+\\)")
                           (repeat . t)
                           (modes quote (erlang-mode))))))

This will add a new alignment rule that aligns comma separated regions with whitespace. It will align all occurrences over all lines in the region, and only be enabled in the Erlang mode.

The alignment will transform this:

[{a, 19, "test", <<0,0>>},
 {abc, 2, "a", <<12,19,28,11>>}].

Into this:

[{a,   19, "test", <<0,0>>},
 {abc, 2,  "a",    <<12,19,28,11>>}].

I set it to Ctrl+E in my .emacs-file:

(global-set-key (read-kbd-macro "C-E") 'align)

I can also recommend the align-repeat macro from Align Commands on Emacs Wiki. I set it to Ctrl+Shift+E.

Synchronized, reliable message passing in Erlang

To do proper, synchronized message passing you need three things: monitoring, timeout and a unique tag in the messages.

Basic Case

The basic case is sending a blocking request to a server process.

request(Pid, Request) ->
    Pid ! {request, Request},
    receive
        {reply, Response} ->
            Response
    end.

This gives us a nice way to send a request to a server and wait for a reply, but it can fail for several reasons:

  • The server process is not alive
  • The server process crashes before it sends an answer
  • The server process takes to long time to respond
  • We receive an old reply to another request

Monitoring

To check if the process is alive or if it crashes during the request is done by monitoring the process. It is also possible to use links, but that requires trapping exits and should not be done dynamically (a process should either trap exits or not when it is started, and keep that behavior for its lifetime).

A monitor is set up by calling erlang:monitor(process, Pid). This will instantly set up a monitor for the process and as soon as the monitored process dies Erlang will send this message to the caller: {'DOWN', MonitorRef, Type, Object, Info} where

  • MonitorRef is the return value from the call erlang:monitor/2
  • Type is the atom process
  • Object is the monitored pid
  • Info is the exit reason for the process or noproc (if the process didn’t exist) or noconnection (for node connections).

The fine thing with erlang:monitor/2 is that it will always send a message to the caller. So if the process is already dead when setting up the monitor, it will still send a down message with the Info set to noproc. This allows us to write quite generic code:

request(Pid, Request) ->
    MRef = erlang:monitor(process, Pid),
    Pid ! {request, Request},
    receive
        {reply, Response} ->
            Response;
        {'DOWN', MRef, process, Pid, Reason} ->
            {error, Reason}
    end.

Timeouts

Even if the server stays alive, it might take too long to respond. Either it is too busy, or there is a bug making it take to long to process the request or it froze. This can be fixed with a receive timeout:

request(Pid, Request, Timeout) ->
    MRef = erlang:monitor(process, Pid),
    Pid ! {request, Request},
    receive
        {reply, Response} ->
            Response;
        {'DOWN', MRef, process, Pid, Reason} ->
            {error, Reason}
    after Timeout ->
            {error, timeout}
    end.

Unique Messages

The last feature is added to avoid mixing up responses to requests made from the same process. The timeout we added in the last part can actually result in the server replying after the client timed out and stopped caring about the response. This will result in the response ending up in the mail box of the client process, which could be accidentally picked up the next time we wait for a response. To avoid this we create a unique reference using the built in function make_ref/0:

request(Pid, Request, Timeout) ->
    MRef = erlang:monitor(process, Pid),
    Ref = make_ref(),
    Pid ! {request, Ref, Request},
    receive
        {reply, Ref, Response} ->
            Response;
        {'DOWN', MRef, process, Pid, Reason} ->
            {error, Reason}
    after Timeout ->
            {error, timeout}
    end.

This assumes the server responds with the same reference that the request contained. Just remember to flush the mailbox for any old responses lying around, otherwise that will be a memory leak!

To avoid the verbose case statements in Erlang, I sometimes use this function:

ifc(true,  True,  _) -> True;
ifc(false, _, False) -> False;

So instead of writing this:

case proplists:is_defined(empty, Options) of
    true  -> [];
    false -> [some_default]
end

one can write this:

ifc(proplists:is_defined(empty, Options), [], [some_default])

The idea was to mimic the simplicity of C’s (and other languages) ? operator.