Erlang

Contributed by Chris Rathman

Rectangle class (rectangle.erl)

-module(rectangle).
-author('ChrisRathman').

-export([new/4,slots/4,dispatch/1]).

% declare record to hold the slots for the class
-record(
  rectangle, {
     x,
     y,
     width,
     height
  }).

% populate the slots of the object record
slots(X, Y, Width, Height) ->
  #rectangle {
     x = X,
     y = Y,
     width = Width,
     height = Height}.

% create a process for the rectangle instance
new(X, Y, Width, Height) ->
  This = slots(X, Y, Width, Height),
  spawn(rectangle, dispatch, [This]).

% dispatch the messages for the process as they are received
dispatch(This) ->
  receive
     {Pid, getx} ->
        Pid!{retval, getx(This)},
        dispatch(This);
     {Pid, gety} ->
        Pid!{retval, gety(This)},
        dispatch(This);

     {setx, X} ->
        dispatch(setx(This, X));
     {sety, Y} ->
        dispatch(sety(This, Y));

     {moveto, X, Y} ->
        dispatch(moveto(This, X, Y));
     {rmoveto, X, Y} ->
        dispatch(rmoveto(This, X, Y));

     {Pid, getwidth} ->
        Pid!{retval, getwidth(This)},
        dispatch(This);
     {Pid, getheight} ->
        Pid!{retval, getheight(This)},
        dispatch(This);

     {setwidth, Width} ->
        dispatch(setwidth(This, Width));
     {setheight, Height} ->
        dispatch(setheight(This, Height));

     {Pid, draw} ->
        draw(This),
        Pid!{retval, true},
        dispatch(This);

     dispose ->
        true
  end.

% get the x & y coordinates for the object
getx(This) ->
  This#rectangle.x.
gety(This) ->
  This#rectangle.y.

% set the x & y coordinates for the object
setx(This, X) ->
  This#rectangle{x = X}.
sety(This, Y) ->
  This#rectangle{y = Y}.

% move the x & y position of the object
moveto(This, X, Y) ->
  setx(sety(This, Y), X).
rmoveto(This, DeltaX, DeltaY) ->
  moveto(This, getx(This) + DeltaX, gety(This) + DeltaY).

% get the width & height of the object
getwidth(This) ->
  This#rectangle.width.
getheight(This) ->
  This#rectangle.height.

% set the width and height of the object
setwidth(This, Width) ->
  This#rectangle{width = Width}.
setheight(This, Height) ->
  This#rectangle{height = Height}.

% draw the rectangle
draw(This) ->
  io:format('Drawing a Rectangle at:('),
  io:write(getx(This)),
  io:format(','),
  io:write(gety(This)),
  io:format('), width '),
  io:write(getwidth(This)),
  io:format(', height '),
  io:write(getheight(This)),
  io:format("~n").

Circle class (circle.erl)

-module(circle).
-author('ChrisRathman').

-export([new/3,slots/3,dispatch/1]).

% declare record to hold the slots for the class
-record(
  circle, {
     super,
     x,
     y,
     radius
  }).

% populate the slots of the object record
slots(X, Y, Radius) ->
  #circle {
     x = X,
     y = Y,
     radius = Radius}.

% create a process for the circle instance
new(X, Y, Radius) ->
  This = slots(X, Y, Radius),
  spawn(circle, dispatch, [This]).

% dispatch the messages for the process as they are received
dispatch(This) ->
  receive
     {Pid, getx} ->
        Pid!{retval, getx(This)},
        dispatch(This);
     {Pid, gety} ->
        Pid!{retval, gety(This)},
        dispatch(This);

     {setx, X} ->
        dispatch(setx(This, X));
     {sety, Y} ->
        dispatch(sety(This, Y));

     {moveto, X, Y} ->
        dispatch(moveto(This, X, Y));
     {rmoveto, X, Y} ->
        dispatch(rmoveto(This, X, Y));

     {Pid, getradius} ->
        Pid!{retval, getradius(This)},
        dispatch(This);

     {setradius, Radius} ->
        dispatch(setradius(This,Radius));

     {Pid, draw} ->
        draw(This),
        Pid!{retval, true},
        dispatch(This);

     dispose ->
        true
  end.

% get the x & y coordinates for the object
getx(This) ->
  This#circle.x.
gety(This) ->
  This#circle.y.

% set the x & y coordinates for the object
setx(This, X) ->
  This#circle{x = X}.
sety(This, Y) ->
  This#circle{y = Y}.

% move the x & y position of the object
moveto(This, X, Y) ->
  setx(sety(This, Y), X).
rmoveto(This, DeltaX, DeltaY) ->
  moveto(This, getx(This) + DeltaX, gety(This) + DeltaY).

% get the radius of the object
getradius(This) ->
  This#circle.radius.

% set the radius of the object
setradius(This, Radius) ->
  This#circle{radius = Radius}.

% draw the circle
draw(This) ->
  io:format('Drawing a Circle at:('),
  io:write(getx(This)),
  io:format(','),
  io:write(gety(This)),
  io:format('), radius '),
  io:write(getradius(This)),
  io:format("~n").

Polymorph test module (polymorph.erl)

-module(polymorph).
-author('ChrisRathman').

-export([tryme/0]).

% test polymorphism in Erlang
tryme() ->
  % create a list containing various shape process instances
  Scribble = [
     rectangle:new(10,20,5,6),
     circle:new(15,25,8)],

  % iterate through the list and handle shapes polymorphically
  drawloop(Scribble),

  % dispose of the processes
  disposeloop(Scribble),

  % call a rectangle specific function
  ARectangle = rectangle:new(0,0,15,15),
  ARectangle!{setwidth, 30},
  ARectangle!{self(), draw},
  retrieve(),
  ARectangle!dispose,
  true.

% iterate through the list of shapes
drawloop([]) -> true;
drawloop([Shape|Tail]) ->
  Shape!{self(), draw},
  retrieve(),
  Shape!{rmoveto, 100, 100},
  Shape!{self(), draw},
  retrieve(),
  drawloop(Tail).

% close out the object processes
disposeloop([]) -> true;
disposeloop([Shape|Tail]) ->
  Shape!dispose,
  disposeloop(Tail).

% wait for process to return result
retrieve() ->
  receive
     {retval, Any} -> Any
  end.

Compiling and Running

>file:set_cwd('/erlang').
>c('rectangle').
>c('circle').
>c('polymorph').
>polymorph:tryme().

Output

Drawing a Rectangle at:(10,20), width 5, height 6
Drawing a Rectangle at:(110,120), width 5, height 6
Drawing a Circle at:(15,25), radius 8
Drawing a Circle at:(115,125), radius 8
Drawing a Rectangle at:(0,0), width 30, height 15
true

Chris Rathman / Chris.Rathman@tx.rr.com