Erlang (programming language)/Tutorials/erlangOOP: Difference between revisions

From Citizendium
Jump to navigation Jump to search
imported>Eric Evers
(New page: Objects with Erlang Erlang is a functional programming language and a concurrency oriented programming languages (Armstrong, 2005), but Erlang does not have explicit built-in object orien...)
 
imported>Eric Evers
mNo edit summary
Line 3: Line 3:
Erlang is a functional programming language and a concurrency oriented programming languages (Armstrong, 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 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.  
Erlang is a functional programming language and a concurrency oriented programming languages (Armstrong, 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 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. Each class
Included is some example code that creates OOP using the described technique.
In the program, we create an instance of the class integer.
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 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 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.
Its parent matrix, knows how to take the square root of a diagonal matrix.  


If a process does not know how to do something, it passes it to its parent(process in this situation).
If a process does not know how to do something, it passes it to its parent(process in this situation).

Revision as of 07:24, 29 October 2008

Objects with Erlang

Erlang is a functional programming language and a concurrency oriented programming languages (Armstrong, 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 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.

If a process does not know how to do something, it passes it 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.

----------------------------------------------------------------
 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  |
+---------+     +---------+ 
   / \             / \     
    |               |     
    |               |      
+---------+     +---------+
| Matrix  |     | Matrix  |
+---------+     +---------+
| sqrt()  |     | sqrt()  |
+---------+     +---------+
   / \             / \     
    |               |      
    |               |      
+---------+     +---------+
| Complex |     | Complex |
+---------+     +---------+
| 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.