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