defining behaviours (Re: Design Patterns for Erlang)

Ulf Wiger <>
Thu Feb 15 10:40:02 CET 2001


On Thu, 15 Feb 2001, Richard Carlsson wrote:

>There is a recent Master's thesis about design patterns and Erlang:
>
>    ftp://ftp.csd.uu.se/pub/papers/masters-theses/0178-ekstrom.pdf
>
>
>	/Richard Carlsson

Good thesis (I've just browsed it.)


I'm currently looking at how behaviours are defined.
Personally, I don't like the idea of poking into otp_internal.erl
in order to define a new behaviour. In practice, this means that 
users of OTP can't define their own patterns.

I would like to propose the following:

- The otp_internal:behaviours() function is simply scrapped.
  If a module defines a compiler directive -behaviour(B), then
  B should correspond to the name of the module implementing the
  behaviour.

- In each behaviour, export a function B:behaviour_info(Category),
  for example like this:


-module(gen_server).
...
-export([behaviour_info/1]).


behaviour_info(exports) ->
  [{init, 1}, {handle_call, 3}, {handle_cast, 2}, {handle_info, 2},
   {terminate, 2}, {code_change, 3}];
behaviour_info(_) ->
  [].


The catch-all clause is meant to facilitate additional categories.




Furthermore, I'd like to introduce the following requirement:

- A behaviour which wants a process must implement a function
  B:init_it/6, with the following arguments:

init_it(Starter, Parent, Name, Mod, Args, Options) ->
  ... % expected never to return


This is not a tough requirement to meet for existing behaviours.
Only two behaviours do not do this already: supervisor and 
supervisor_bridge. In supervisor_bridge, the function could look like
this:

init_it(Starter, Parent, Name, Mod, Args, Options) ->
    IArgs = init_args(Args),
    gen_server:init_it(Starter, Parent, Name, Mod, IArgs, Options).


init_args([Mod, StartArgs]) ->
    [Mod, StartArgs, self];
init_args([Mod, StartArgs, Name]) ->
    [Mod, StartArgs, Name].


That is, the function essentially calls gen_server:init_it/6, since
supervisor_bridge is implemented as a gen_server.



Why do this?

Because I want to be able tell a supervisor to start a generic
server, or other behaviour, and have the supervisor take care of 
actually starting the process. Three immediate advantages come to 
mind:

- The supervisor can make sure that the process is started with 
  spawn_link(); Today, if a gen_server is started with e.g. 
  gen_server:start(), supervision will be ineffective (no link.)

- The supervisor is able to execute in the started process before
  handing over control to the behaviour. This way, the supervisor
  can e.g. log information about _why_ the process is started
  (initial start?, restart?, escalated restart?) -- something that
  is not possible today.

- The child start specification becomes more descriptive. Here's
  an example of what it could look like:

  {myServer, gen_server, 
   [{module, myServer},
    {args, []},
    {regname, {local, myServer}}],
   permanent, 2000, worker, [myServer]}

As you might have guessed, I've written such a supervisor.
I will post it shortly.

(As some of you know, we at AXD 301 also specify our permanent
processes in the .app file, and let a central start function take
care of building the supervision hierarchy. The reason for this is 
to make the process structure more visible.)


A still open issue is whether behaviours which do _not_ want a 
process should also be allowed? Actually, we have one already:
application. How would they be defined? Is there a need for 
additional rules?

Comments are welcome.

/Uffe
-- 
Ulf Wiger                                    tfn: +46  8 719 81 95
Senior System Architect                      mob: +46 70 519 81 95
Strategic Product & System Management    ATM Multiservice Networks
Data Backbone & Optical Services Division      Ericsson Telecom AB




More information about the erlang-questions mailing list