Structs (was RE: Record selectors)

Chris Pressey <>
Wed Feb 12 01:00:26 CET 2003


On Mon, 27 Jan 2003 13:43:41 +0100 (MET)
Bengt Kleberg <> wrote:

> 
> 
> > On Wed, 15 Jan 2003 09:18:38 +0100
> > "Vlad Dumitrescu (EAW)" <> wrote:
> > 
> ...deleted
> > From the OO literature I've read, three tenets seems to be universally
> > agreed upon: encapsulation, polymorphism, and inheritance.  Two others
> > get mentioned often enough, abstraction and identity, but they seem
> > less well understood.
> 
> sorry about this late email, but i had to get my copy of ''object
> oriented software construction'', by bertrand meyer, back before i
> wrote anything.

Sorry, I've been a bit busy too.

> in chapter 2, ''criteria of object orientation'' there are 15 pages
> devoted to what object orientation (o-o) is. there are 3 cathegories
> 1 method and language (20 criteria)
> 2 implementation and environment (5 criteria)
> 3 libraries (4 criteria)
> also mentioned is the fact that o-o is not a boolean concept.
> ''environment a, although not 100% o-o, may be more o-o than
> environment b''.

I think that terms like OO (or OTP) sometimes take on far too
wide-reaching a meaning, and become impractical for debate  :)
Having 29 criteria makes a pretty hefty definition.

To me, OO just means code and data (state) are grouped in units.  All the
other tenets I've seen seem to follow from that principle.  Erlang modules
currently work fairly well for the purpose, and with enough effort I'm
sure any OO technique could be implemented on top of them; but more
usually, I'll bet Erlang programmers just code up discrete abstractions to
serve the specific purposes they have in mind right then.

In other words, I find it's really not that different programming in
Erlang than programming in your typical OO language, but you build what
you need and only what you need of the OO framework when you need it. 
Modules I write tend to look a lot like:

  -module(rectangle).

  -record(rectangle, {width, length}).

  new(Config) -> config(#rectangle{}, Config).

  config(Rectangle, []) ->
    Rectangle;
  config(Rectangle, [H | T]) ->
    config(config(Rectangle, H), T);
  config(Rectangle, {width, N}) when number(N) ->
    Rectangle#rectangle{ width = N };
  config(Rectangle, {length, N}) when number(N) ->
    Rectangle#rectangle{ length = N }.

  width(Rectangle) ->
    Rectangle#rectangle.width.
  length(Rectangle) ->
    Rectangle#rectangle.length.

  perimeter(Rectangle) ->
    2 * (Rectangle#rectangle.width + Rectangle#rectangle.length).

  etc.

Of course it varies greatly.  When it's a server instead of an inert lump
of data, new/1 might be called start/1, and it might spawn a process, with
which the other functions might communicate with message sending or
gen_server calls.  It might also create ets tables.  Setting properties
might have width/2 and length/2 functions, or a read/2 function, for
convenience.  The functions might return {ok, Rectangle} or {error,
Rectangle} instead of just a new Rectangle.  Etc.  But it's basically the
same.

If an external module needs the underlying representation for whatever
reason, it can be exported by a function called record_info/1 which wraps
around the record_info macro.

The association between the names serves to make the grouping adhere: if
the module is named rectangle, the instance variables are named
Rectangle or RectangleSomething, the ets tables and other global names
should be named rectangle too (and the ?MODULE macro makes this easy.)

Am I a freak, or do other people find that it helps to write modules this
way - basically, to hide each data structure behind interface code?
Would structs obsolete this discipline, and if not, how could they be
enriched to do so?

-Chris



More information about the erlang-questions mailing list