diff --git a/docs/week04/index.html b/docs/week04/index.html index 97ef573..35dfb9b 100644 --- a/docs/week04/index.html +++ b/docs/week04/index.html @@ -123,7 +123,7 @@

Getting Started with Processes

-		Calc_pid = spawn(simple_calc,run,[]).
+		1> Calc_pid = spawn(simple_calc,run,[]).
 

The three parameters for spawn are; 1) the name of the module containing the function, the name of the function to start up, and the list of parameters that function needs. @@ -140,7 +140,7 @@

Getting Started with Processes

-		Calc_pid ! {self(),add,[1,2,3]}.
+		2> Calc_pid ! {self(),add,[1,2,3]}.
 

On the left of the ! operator you see the process ID for the process the message is being sent to. On the right is the message tuple that @@ -168,26 +168,26 @@

Getting Started with Processes

-		receive Resp -> Resp end.
+		3> receive Resp -> Resp end.
 

This line of code says,"Wait until you receive a message. When you get a message, put it in Resp. Then stop waiting."

So there you've seen the basics of starting a process, sending it messages, and getting response messages. A little later in this reading you'll see - how to automate this to make it even easier, but right now it's a good idea to see how to write the code for a process. We'll use the simp_calc + how to automate this to make it even easier, but right now it's a good idea to see how to write the code for a process. We'll use the simple_calc process since you're already a little familiar with it. This example covers the basics.

So, then what does creating, starting, and using a process look like in Erlang? Well, just like your friends, an Erlang process needs to have some way to receive a message, make sure it understands the message, and then send a response. Also, just like your friends, the process shouldn't die after it helps you out. 😇 Let's start by looking at the code for an Erlang process that performs simple tasks that won't distract from - learning process structure and use. Let's create a ridiculously simple calculator process called simp_calc by putting functions in a - simp_calc module, including documentation. + learning process structure and use. Let's create a ridiculously simple calculator process called simple_calc by putting functions in a + simple_calc module, including documentation.

-		-module(simp_calc).
+		-module(simple_calc).
 -export([run/0]).
 
 %% @doc The <kbd>run/0</kbd> function is the service keep-alive function. 
@@ -236,23 +236,24 @@ 

Getting Started with Processes

run()->
     receive
-        {Pid,multiply,List} ->
+        {Pid, {multiply,List}} ->
               Pid ! {ok,lists:foldl(fun(X,Y)->X*Y end,1,List)};
-        {Pid,add,List} ->
+        {Pid, {add,List}} ->
               Pid ! {ok,lists:foldl(fun(X,Y)->X+Y end,0,List)};
-        {Pid,divide,Dividend,Divisor} -> 
+        {Pid, {divide,Dividend,Divisor}} -> 
               Pid ! {ok,Dividend div Divisor};
-        {Pid,_Other} ->
+        {Pid, _Other} ->
               Pid ! {fail, unrecognized_message}
     end,
     run().
 

+

Notice how we've changed the pattern slightly so each receive clause has exactly two things in the tuple, a Pid and a tuple indicating the command. The last pattern will handle any message that does not include a valid command (i.e., multiply, add, divide).

You may be thinking, "But what if they don't send a process ID to respond too?" Good question. Don't put in an error handling match for that. Let them be responsible for their own actions. 😇 Besides, you couldn't tell them they did it wrong anyway. They will get a message that says something like this.

-

{badarg,[{simp_calc,run,0,[{file,"simp_calc.erl"},{line,33}]}]}
+	

{badarg,[{simple_calc,run,0,[{file,"simple_calc.erl"},{line,33}]}]}
 

If you get a message that looks like that when you send a message to a process, you know you didn't put the message together correctly. @@ -276,15 +277,18 @@

Sending Messages Made Easy

Here is a code snippet showing the use of calculate in the REPL. I put it in a module with the ridiculous name calc_it. -

Pid = spawn(simp_calc,run,[]).
-calc_it:calculate(Pid,{add, [1,2,3,4,5]},fun(R) -> io:format("Response: ~p~n",[R]) end).
+

1> Pid = spawn(simple_calc,run,[]).
+2> calc_it:calculate(Pid,{add, [1,2,3,4,5]},fun(R) -> io:format("Response: ~p~n",[R]) end).
 

Notice that in the fun passed in, all I'm doing is printing out the result. I just wanted to keep the example simple. You could do things that are a lot more interesting.

-

As one last step, let's update the run/0 function slightly to make it easier to pass in a Message.

-
run()->
+

To recap, here's all the code for the simple_calc module and the calc_it module.

+
-module(simple_calc).
+-export([run/0]).
+	
+run()->
     receive
         {Pid, {multiply,List}} ->
               Pid ! {ok,lists:foldl(fun(X,Y)->X*Y end,1,List)};
@@ -297,6 +301,16 @@ 

Sending Messages Made Easy

end, run().
+
-module(calc_it).
+-export([calculate/3]).
+
+calculate(Pid, Message, Response_handler) ->
+    Pid ! {self(), Message},
+    receive
+        Response ->
+            Response_handler(Response)
+    end.
+

Client-Server

So, unbeknownst to you, the two modules you just saw fall into a class of computing called client-server. The process in the simple_calc @@ -304,6 +318,47 @@

Client-Server

Sometimes we use the term to refer to the hardware, virtual or not, that the server runs on, but the reason we do that is because of the SOFTWARE!

+

Putting it all together

+

Now that you've seen how to implement a simple concurrent calculator using two modules, simple_calc and calc_it, let's combine the client and server code into a single module following a pattern you'll see repeatedly in Erlang.

+

We'll add a function called start/0 which will take care of spawning the server process for us. Then we'll add the client function calculate/3.

+
-module(simple_calc_1).
+-export([start/0, run/0, calculate/3]).
+
+% We can add a start() function so we don't have to spawn it manually from the
+% REPL
+start() -> 
+	spawn(?MODULE, run, []).
+
+run() ->
+	receive
+		{From, {add, Arguments}} -> 
+			Result = lists:foldl(fun(X, Acc) -> X + Acc end, 0, Arguments),
+			From ! {ok, Result};
+		{From, {multiply, Arguments}} -> 
+			Result = lists:foldl(fun(X, Acc) -> X * Acc end, 1, Arguments),
+			From ! {ok, Result};
+		{From, {divide, Dividend, Divisor}} -> 
+			Result = Dividend div Divisor,
+			From ! {ok, Result};
+		{From, Message} ->
+			From ! {fail, Message}
+	end,
+	run().
+
+
+calculate(Pid, Message, Response_handler) ->
+	Pid ! {self(), Message},
+	receive
+		Response ->
+			Response_handler(Response)
+	end.	
+
+

To use the calculator, we first start the server, then send messages to the server using the calculate/3 function.

+
1> Pid = simple_calc_1:start().
+2> simple_calc_1:calculate(Pid, {add, [1,2,3,4,5]}, fun(R) -> io:format("Response: ~p~n", [R]) end).
+
+ +

Wrap Up

Now that you have seen how to do basic client-server processes, you are ready, next week, to learn to take it one step further. You'll learn how to make what is called a stateful-process.