• chevron_right

      LFE Swank Server - progress

      Michał "phoe" Herda · Sunday, 23 December, 2018 - 11:58

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

    #erlang #lfe #swank

    I have created a lot more structure inside the LFE Swank server. LFE is rather fun to develop in and I am getting used to the quirks of Erlang beneath and the pain that is associated with writing simple and concise tests. (Who knows, maybe I'll be able to use macros to ease it up somehow?...)

    And I have also possibly found a bug in LFE. We'll see if I'm wrong (the better option) or if LFE is wrong (the worse one). Update: both. LFE had a bug in v1.2.1, but it was noticed and fixed in v1.3.

    • chevron_right

      WITH_PROCESSES - utility macro for Lisp Flavored Erlang

      Michał "phoe" Herda · Sunday, 23 December, 2018 - 11:56 · 1 minute

    (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"))))))