Structs

Vladimir Sekissov <>
Tue Feb 11 20:54:45 CET 2003


Good day,

If you really want or need to code in OO-manner classical dispatching
would be more flexible:

-module(geometry).

new(rectangle, Data) ->
  rectangle:new(Data).

width(Obj) ->
  Obj(width).

length(Obj) ->
  Obj(length).

perimeter(Obj) ->
 Obj(perimeter).


-module(rectangle).

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

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

mk_obj(Rectangle) ->
  fun (length) ->
     length(Rectangle);
      (width) ->
     width(Rectangle);
      (perimeter) ->
     perimeter(Rectangle)
  end.

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

Best Regards,
Vladimir Sekissov

cpressey> Modules I write tend to look a lot like:
cpressey> 
cpressey>   -module(rectangle).
cpressey> 
cpressey>   -record(rectangle, {width, length}).
cpressey> 
cpressey>   new(Config) -> config(#rectangle{}, Config).
cpressey> 
cpressey>   config(Rectangle, []) ->
cpressey>     Rectangle;
cpressey>   config(Rectangle, [H | T]) ->
cpressey>     config(config(Rectangle, H), T);
cpressey>   config(Rectangle, {width, N}) when number(N) ->
cpressey>     Rectangle#rectangle{ width = N };
cpressey>   config(Rectangle, {length, N}) when number(N) ->
cpressey>     Rectangle#rectangle{ length = N }.
cpressey> 
cpressey>   width(Rectangle) ->
cpressey>     Rectangle#rectangle.width.
cpressey>   length(Rectangle) ->
cpressey>     Rectangle#rectangle.length.
cpressey> 
cpressey>   perimeter(Rectangle) ->
cpressey>     2 * (Rectangle#rectangle.width + Rectangle#rectangle.length).
cpressey> 
cpressey>   etc.
cpressey> 
cpressey> Of course it varies greatly.  When it's a server instead of an inert lump
cpressey> of data, new/1 might be called start/1, and it might spawn a process, with
cpressey> which the other functions might communicate with message sending or
cpressey> gen_server calls.  It might also create ets tables.  Setting properties
cpressey> might have width/2 and length/2 functions, or a read/2 function, for
cpressey> convenience.  The functions might return {ok, Rectangle} or {error,
cpressey> Rectangle} instead of just a new Rectangle.  Etc.  But it's basically the
cpressey> same.
cpressey> 
cpressey> If an external module needs the underlying representation for whatever
cpressey> reason, it can be exported by a function called record_info/1 which wraps
cpressey> around the record_info macro.
cpressey> 
cpressey> The association between the names serves to make the grouping adhere: if
cpressey> the module is named rectangle, the instance variables are named
cpressey> Rectangle or RectangleSomething, the ets tables and other global names
cpressey> should be named rectangle too (and the ?MODULE macro makes this easy.)
cpressey> 
cpressey> Am I a freak, or do other people find that it helps to write modules this
cpressey> way - basically, to hide each data structure behind interface code?
cpressey> Would structs obsolete this discipline, and if not, how could they be
cpressey> enriched to do so?
cpressey> 
cpressey> -Chris



More information about the erlang-questions mailing list