[erlang-questions] Avoiding boilerplate code when using gen_server

Garrett Smith g@REDACTED
Wed Mar 21 16:29:51 CET 2012


On Wed, Mar 21, 2012 at 6:48 AM, Torben Hoffmann
<torben.lehoff@REDACTED> wrote:
> Hi,
>
> I was sitting with a co-worker - who is proficient in OCaml and lots of
> other stuff and he is now learning Erlang - and then he comes up with a good
> question on the amount of boilerplate code when implementing a gen_server.

I've had the exact same conversation with coworkers -- it led to this:

http://e2project.org/

> From the API functions you call out to gen_server:call/2 and on top of that
> you need to implement a handle_call/3 function clause that matches whatever
> format you used for wrapping the incoming message.
>
> The question was: why don't you use an IDL (Interface Definition Language)
> approach instead of writing all that code that has to match up in order to
> work?

Erlang has a "parse transform" feature that would let you embed such
an "IDL" in a single module, and could generate the "boilerplate".

I initially started down this road with e2, but decided against it for
these reasons:

- Erlang is already a *very* simple language - let's just use it
- Auto generated functions are hard to document, test, etc.
- Hiding the client-server distinction in Erlang is a terrible disservice

IMO, e2 solves the problem of "too much boiler plate". It's really
easy and requires zero magic.

Here's a "gen_server" equivalent in e2:

https://github.com/gar1t/e2v2/blob/master/examples/ping/src/ping_server.erl

You might object to the separation of "ping" and "handle_msg" -- it
appears to be just one function, so why break it up into two pieces?

The problem is that it's not one function -- it's definitely *two*
very separate pieces of code.

When you write a gen_server style module, you're writing code that
support client-server interactions. You have, in the same module, code
that is called by the "client" and code that is called by the
"server". If you don't grok this, you're missing the entire point of
this type of module.

Code that hides this difference -- e.g. a parse transform that gloms
the client and server functions into one -- is IMO a Really Bad Idea.

As an example of how this client/server difference is important,
consider form validation in a web app. There are two places you can
run code to validate form input -- you can validate it in the browser
using JavaScript, or you can send the form data to the server. Web
developers may opt for browser validation to avoid the expense of
sending data to the server -- or they might prefer it on the server.
Either way, it's an important consideration.

This same dynamic applies to gen_server style modules. If you stick
with it, I think you'll appreciate the separateness of client and
server facing code.

As for the boilerplate, I couldn't agree with you more!

Garrett

P.S. I'm giving a talk on e2 at the SF Factory in a couple weeks,
where I'll get into this in more details. Also, the e2 docs and github
project are in slight disarray at the moment, but will be put in order
this weekend!



More information about the erlang-questions mailing list