Erlang (programming language)/Tutorials/erlangOOP

From Citizendium
Jump to navigation Jump to search

Objects with Erlang

Erlang is a functional programming language and a concurrency oriented programming languages (Armstrong, disertation, 2005), but Erlang does not have explicit built-in object oriented language features. An object oriented programming style can be achieved by alternative means easily. It is particularly easy to do object oriented programming if we restrict ourselves to single inheritance. One can use processes to represent classes and messages to represent methods. To do so, each object when created can create a chain of processes that represent their ancestors in the inheritance chain. The methods(messages) can be passed up the chain until they reach a process that has a matching method. If the message reaches the top of the chain(the Object class or above that dev_nul) then we can generate an exception for a "bad method name". Each class(process) will maintain its own attributes(variables) in its own recursive argument call list. These attributes can be accessed and updated with messages such as get and set. Attributes are stored in a dictionary called a Bundle, in each class process.

Included is some example code that creates OOP using the described technique. In the program, we create an instance of the class integer. Its parent float, knows how to take the square root of reals. Its parent complex, knows how to take the square root of negative numbers. Its parent matrix, knows how to take the square root of a diagonal matrix.

Logically, the traditional class relationship is held in the class diagram. An integer is a Real. A Real(float) is a (subset of) complex. An a complex number is a (subset of) complex matrices, if we think of a (1 by 1) matrix as a single number.

A message(method) is sent to an instance of an object. If a process does not know how to do something, it passes a message(method) to its parent(process in this situation). If we tried to do something like take the sqrt(non-diag-matrix) it would be passed up to dev_nul and generate an error.

The start function creates an instance of the integer class. Then it asks the instance to calculate the square_root of 4 numbers: 4, 0.04, -4 and the matrix [[4,0],[0,9]]. The answers are: 2, 0.2, {2, i} and [[2,0],[0,3]].

----------------------------------------------------------------
 Original        Each object has its own process for each of its ancestors 
 Classes         (Process chain for object Obj_1)
 
+---------+     +---------+
| dev_nul |     | dev_nul |
+---------+     +---------+
   / \             / \    
    |               |    
    |               |      
+-----------+   +-----------+ 
| Object    |   | Object    |
+-----------+   +-----------+
| id        |   | id        |
| classname |   | classname |
| name      |   | name      |
+-----------+   +-----------+
| get()     |   | get()     |
+-----------+   +-----------+ 
   / \             / \     
    |               |     
    |               |      
+---------+     +---------+
| Matrix  |     | Matrix  |
+---------+     +---------+
+---------+     +---------+
| sqrt()  |     | sqrt()  |
+---------+     +---------+
   / \             / \     
    |               |      
    |               |      
+---------+     +---------+
| Complex |     | Complex |
+---------+     +---------+
+---------+     +---------+
| sqrt()  |     | sqrt()  |
+---------+     +---------+ 
   / \             / \     
    |               |      
    |               |     
+---------+     +---------+
| Float   |     | Float   |
+---------+     +---------+
+---------+     +---------+
| sqrt()  |     | sqrt()  |
+---------+     +---------+ 
   / \             / \     
    |               |      
    |               | 
+---------+     +---------+
| Integer |     | Integer |
+---------+     +---------+
+---------+     +---------+
| sqrt()  |     | sqrt()  |
+---------+     +---------+
---------------------------------------
Program output:
1> mathobjects:start().
[
[{id,#Ref<0.0.0.27>},{class_name,integer},{name,book}],
2.00000, 0.20000,
{2.00000, i},
[[2.00000,0],[0,3.00000]]
]
---------------------------------------
-module(mathobjects).
-compile(export_all).

start()->
       Obj_1         = spawn_link(mathobjects, integer, []),  
	Id_1 	      = rpc(Obj_1, {get, id}),             
	Name_1        = rpc(Obj_1, {get, name}),           
	Class_Name_1  = rpc(Obj_1, {get, class_name}),     
	% -------------------------------
	R0 = [ Id_1, Class_Name_1, Name_1 ],
	R1 = rpc(Obj_1, {sqrt, 4}),
	R2 = rpc(Obj_1, {sqrt, 0.04}),
	R3 = rpc(Obj_1, {sqrt, -4}),
	R4 = rpc(Obj_1, {sqrt, [[4,0],[0,9]]}),
	[R0, R1, R2, R3, R4].
	
rpc(Dest, Msg) ->
	Dest ! {self(), Msg},
	receive
		Answer -> 
			Answer
	after 1000 ->
		ok
	end.

dev_null() ->
	receive
		{From, Any} -> From ! {Any, attribute_unknown}
	end,
	dev_null().

object() ->
	Id 		= erlang:make_ref(),
	Class_Name 	= object,
	Bundle 		= dict:from_list([ {id,Id}, {class_name, Class_Name} ]),
	Parent 		= spawn(objects, dev_null, []),
	object(Parent, Bundle).

object(Parent, Bundle) ->
	receive
		{From, {get, Attribute}} ->
			handle_get_attribute(Attribute, From, Bundle, Parent)
	end,
	object(Parent, Bundle).

% default constructor 

matrix() ->
	Class_Name      = matrix,
	Name            = book,
	Parent 	        = spawn_link(mathobjects, object, []),
	Parent_Class    = object,
	Bundle = dict:from_list( [
		{class_name, Class_Name},
		{parent_class, Parent_Class},
		{name, Name},
		{parent, Parent}]),
	matrix(Parent, Bundle).
	
matrix(Parent, Bundle) ->
	receive
		{From, {get, Attribute}} ->
			handle_get_attribute(Attribute, From, Bundle, Parent);
		{set, Attribute, Value} ->
			NBundle = handle_set_attribute(Attribute, Value, Bundle, Parent),
			matrix(Parent, NBundle);
		{From, {sqrt, [[A,B],[C,D]]}} when B==0, C==0 ->
			Out = [[math:sqrt(A),0],[0,math:sqrt(D)]],
			From ! Out; 
		Any ->
			Parent ! Any
	end,
	matrix(Parent, Bundle).
	
complex() ->
	Class_Name      = complex,
	Name            = book,
	Parent 	        = spawn_link(mathobjects, matrix, []),
	Parent_Class    = object,
	Bundle = dict:from_list( [
		{class_name, Class_Name},
		{parent_class, Parent_Class},
		{name, Name},
		{parent, Parent} ] ),
	complex(Parent, Bundle).
	
complex(Parent, Bundle) ->
  	receive
		{From, {get, Attribute}} ->
			handle_get_attribute(Attribute, From, Bundle, Parent);
		{set, Attribute, Value} ->
			NBundle = handle_set_attribute(Attribute, Value, Bundle, Parent),
			complex(Parent, NBundle);
		{From, {sqrt, Arg}} when is_list(Arg) ->
			Parent ! {From, {sqrt, Arg}};
		{From, {sqrt, Arg}} when Arg < 0 ->
			Out = {math:sqrt(0-Arg), i},
			From ! Out;
		Any ->
			Parent ! Any
	end,
	complex(Parent, Bundle).
	
float() ->
 	Class_Name      = float,
	Name            = book,
	Parent 	        = spawn_link(mathobjects, complex, []),
	Parent_Class    = object,
	Bundle = dict:from_list( [
		{class_name, Class_Name},
		{parent_class, Parent_Class},
		{name, Name},
		{parent, Parent}]),
	float(Parent, Bundle).
	
float(Parent, Bundle) ->
	receive
		{From, {get, Attribute}} ->
			handle_get_attribute(Attribute, From, Bundle, Parent);
		{set, Attribute, Value} ->
			NBundle = handle_set_attribute(Attribute, Value, Bundle, Parent),
			float(Parent, NBundle);
		{From, {sqrt, Arg}} when is_list(Arg) ->
			Out = rpc(Parent, {sqrt, Arg}),
			From ! Out;
		{From, {sqrt, Arg}} when Arg < 0 ->
			Out = rpc(Parent, {sqrt, Arg}),
			From ! Out;  
		{From, {sqrt, Arg}} ->
			Out = math:sqrt(Arg),
			From ! Out; 
		Any ->
			Parent ! Any
	end,
	float(Parent, Bundle).
	
integer() ->
	Class_Name      = integer,
	Name            = book,
	Parent 	        = spawn_link(mathobjects, float, []),
	Parent_Class    = object,
	Bundle = dict:from_list( [
		{class_name, Class_Name},
		{parent_class, Parent_Class},
		{name, Name},
		{parent, Parent}]),
	integer(Parent, Bundle).
	
integer(Parent, Bundle) ->
	receive
		{From, {get, Attribute}} ->
			handle_get_attribute(Attribute, From, Bundle, Parent);
		{set, Attribute, Value} ->
			NBundle = handle_set_attribute(Attribute, Value, Bundle, Parent),
			integer(Parent, NBundle);
		{From, {sqrt, Arg}} when is_float(Arg) ->
			Out = rpc(Parent, {sqrt, Arg}),
			From ! Out;
		{From, {sqrt, Arg}} when is_list(Arg) ->
			Out = rpc(Parent, {sqrt, Arg}),
			From ! Out;
		{From, {sqrt, Arg}} when Arg < 0 ->
			Out = rpc(Parent, {sqrt, Arg}),
			From ! Out;
		{From, {sqrt, Arg}} ->
			Out = 
				try math:sqrt(Arg)  
					catch
						_AnyException ->
							rpc(Parent, {From, sqrt, Arg})
					end,
			From ! Out;
		Any ->
			Parent ! Any
	end,
	integer(Parent, Bundle).
	
% -----------------------------------------------
 	
handle_set_attribute(Attribute, Value, Bundle, Parent) ->	
			Found = dict:find(Attribute, Bundle),
			if 
				is_tuple(Found) ->     % if attribute exists then set it
					{ok, _} = Found,
					NBundle = dict:store(Attribute, Value, Bundle),
					NBundle;
				true ->
					Parent ! {set, Attribute, Value}
			end.
			
handle_get_attribute(Attribute, From, Bundle, Parent) ->	
			Found = dict:find(Attribute, Bundle),
			if 
				is_tuple(Found) ->
					{ok, Value} = Found,
					From ! {Attribute, Value};
				true ->
					Parent ! {From, {get, Attribute}}
			end.