defining behaviours (Re: Design Patterns for Erlang)

Martin Bjorklund mbj@REDACTED
Mon Feb 19 16:27:51 CET 2001


Ulf Wiger <etxuwig@REDACTED> wrote:

> 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 wouldn't go that far - of course users can define new behaviours;
you don't have to modify otp_internal.erl at all.  The only function
provided by otp_internal is that the compiler checks exported
functions, and warns if a function required by the behaviour is
missing.  A behaviour is of course much more than this...  This being
said, I think what you propose (the behaviour 'behaviour' :) below
should have been done a long time ago.  (As many other things, it was
a Qn'D hack which seemed fun at the time.)

> 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(_) ->
>   [].

But I think it should be a bit more detailed, specifically we should
make a distiction between mandatory and optional functions:


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

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

> 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:

Comments below.

> 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.

What do you mean - the supervisor starts the process already...?

> 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 could call link() explicitly to acheive this effect.
We had endless discussions about this when we implemented this...
It's not safe anyway, since the process could call unlink() when it's
started.  monitor is not a good idea either, since the child wants to
be notified if the supervisor dies.  We agreed that the supervision
mechanism as it is today is for co-operating processes.

> - 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.

On the other hand, today the call-back process is able to execute in
the supervisor before the process is started.  This feature can be
used, and is used, in many interesting ways... ;-)

> A still open issue is whether behaviours which do _not_ want a 
> process should also be allowed? Actually, we have one already:
> application.

And gen_event.  They should definitely be allowed!


/martin



More information about the erlang-questions mailing list