close
  • person chevron_right

    WITH_PROCESSES - utility macro for Lisp Flavored Erlang

    Michał "phoe" Herda – Sunday, 23 December - 11:56

(This is a repost of an old blog post of mine from Teknik.)

#lfe #erlang

While working on the LFE Swank Server, I noticed that Erlang's architecture rewards creating multiple tiny specialized processes. Because of this, I will want to have a simple test framework in LFE that leverages this architecture and is able to create "mock" processes. Essentially, what I need is a macro that is capable of spawning processes, binding their PIDs to variables, and cleaning them up after the test is over.

A WITH-PROCESSES macro.

But, since the land of Erlang prefers underscores, I instead named it WITH_PROCESSES.

Writing macros in LFE is a really weird feeling to me, at least when compared to Common Lisp. I have no destructuring lambda lists which I am used to, but I have pattern matching which is slightly more powerful than CL's destructuring. I also got to know Erlang's/LFE's try/catch/after form - from a Lisper's perspective, it's CL:HANDLER-CASE and CL:UNWIND-PROTECT combined.

See the code for the macro and a very simple test case below. To make sure that the processes actually die, substitute the echo function with a call to (timer:sleep 100000), and then call the (i) function inside and just outside the WITH_PROCESSES body to verify there are timer:sleep calls in the process list inside the macro body, but none of them remain after it is finished.

(defmacro with_processes
  (`(() . ,body)
   `(progn ,@body))
  (`(((,symbol ,function) . ,rest) . ,body)
   `(let ((,symbol (spawn ,function)))
      (try (with-processes ,rest ,@body)
        (after (erlang:exit ,symbol 'kill))))))
(defun test_with_processes ()
  (flet ((echo () (receive (`#(,pid ,x) (! pid x)))))
    (with-processes 
     ((foo #'echo/0)
      (bar #'echo/0))
     (! foo `#(,(self) 2))
     (receive (2 'ok)
              (after 1000 (erlang:error "test failed")))
     (! bar `#(,(self) 10))
     (receive (10 'ok)
              (after 1000 (erlang:error "test failed"))))))