pandoragami
pandoragami

Reputation: 5575

erlang function call out of order?

I modified the kitchen module from Learn you some Erlang using standard IO functions to see what gets printed out in the order of execution and I found something really strange. Basically I ran the following in the shell

3> Pid = spawn(kitchen, fridge2, [[baking_soda]]).
<0.41.0>
4> kitchen:store(Pid, water).
0
2
1
ok
ok

It seems to me the function store has a call to the function fridge2 before the receive clause in the store function right after 0 is printed out and then after 2 is printed the receive clause is executed and 1 is finally printed. The modified code is below. How is the fridge2 function called from store function? Is this because of parallel execution somehow? What does this line do {Pid, Msg} -> in the store function? Is it a function call? and why is ok printed?

-module(kitchen).
-compile(export_all).

start(FoodList) ->
    spawn(?MODULE, fridge2, [FoodList]).

store(Pid, Food) ->
    Pid ! {self(), {store, Food}},
    io:format("~p~n", [0]),
    receive                 
        {Pid, Msg} -> 
            io:format("~p~n", [1]),
            io:format("~p~n", [Msg]),
            Msg
    end.

take(Pid, Food) ->
    Pid ! {self(), {take, Food}},
    receive
        {Pid, Msg} -> Msg
    end.

store2(Pid, Food) ->
    Pid ! {self(), {store, Food}},
    receive
        {Pid, Msg} -> Msg
    after 3000 ->
        timeout
    end.

take2(Pid, Food) ->
    Pid ! {self(), {take, Food}},
    receive
        {Pid, Msg} -> Msg
    after 3000 ->
        timeout
    end.

fridge1() ->
    receive
        {From, {store, _Food}} ->
            From ! {self(), ok},
            fridge1();
        {From, {take, _Food}} ->
            %% uh....
            From ! {self(), not_found},
            fridge1();
        terminate ->
            ok
    end.

fridge2(FoodList) ->
    receive
        {From, {store, Food}} ->
            From ! {self(), ok},
            io:format("~p~n", [2]),
            fridge2([Food|FoodList]);
        {From, {take, Food}} ->
            case lists:member(Food, FoodList) of
                true ->
                    io:format("~p~n", [3]),
                    From ! {self(), {ok, Food}},
                    fridge2(lists:delete(Food, FoodList));
                false ->
                    io:format("~p~n", [4]),
                    From ! {self(), not_found},
                    fridge2(FoodList)
            end;
        terminate ->
            ok
    end.

Upvotes: 1

Views: 220

Answers (1)

butter71
butter71

Reputation: 2723

similar to case statements, receive uses pattern matching to determine what clause to execute. {Pid, Msg} is a clause that will match any 2-tuple.

let's walk through you the execution of your code --

Pid = spawn(kitchen, fridge2, [[baking_soda]]).

this spawns a new process that executes the kitchen:fridge2/1 function. that function blocks until it receives a message that is either a 2-tuple of the form {From, {[store|take], Food}} or the atom 'terminate'.

kitchen:store(Pid, water).

meanwhile, you call the above function from the shell. It sends the message {self(), {store, Food}} to that new process, prints "0" and then waits to receive a message that is a 2-tuple.

the other process has now received a message that satisties its receive. It sends the message {self(), ok} back to the process who sent the message, prints "2", recursively calls itself and again waits to receive a message.

the shell process has now received a message and continues execution. it prints "1" and then prints the second element of the tuple it received ("ok"). finally it returns 'ok' to the shell.

the shell prints the result ("ok") and displays a prompt.

the second process is still waiting to receive a message.

Upvotes: 1

Related Questions