I am reading LYAE
chapter on timeouts and i can't wrap my head about what happens in the following scenario:
important() ->
receive
{Priority, Message} when Priority > 10 ->
[Message | important()]
after 0 ->
normal()
end.
normal() ->
receive
{_, Message} ->
[Message | normal()]
after 0 ->
[]
end.
1> c(multiproc).
{ok,multiproc}
2> self() ! {15, high}, self() ! {7, low}, self() ! {1, low}, self() ! {17, high}.
{17,high}
3> multiproc:important().
[high,high,low,low]
I do not understand the following:
after 0
its like using ,
from what i understand, it happens EVEN if the message matches in the receive
clause?[15, important()]
, which at the second iteration will call normal
so we have one one hand [15, important() --3-rd call]
and [7,normal() -fourth call]
.
So how in the end somehow we end up with two lists that get concatenated.important
with [15]
normal
(first call) with [7]
Now normal()
(first call) is already waiting for a new message ,and important
now will make a second call to normal()
so now in the second call won't we have [1]
?
I do not understand how do the [7]
and [1]
get merged since they are from separate calls to normal()
.
I understand for important()
since the result gets placed at the end of the list [Message,important()]
.But that is not the case for normal()
since it gets called by important
and everytime it should create a new list.
P.S I have added a picture , to further explain my dilemma.I think i understand now that at the end the 2 branches will return their result into [7,15]
but i still do not understand in what order.
In a receive
, we'll end up either in one of the matching clauses or in the after
clause. That is, the after
clause will only be evaluated if there is no matching message within the timeout. This is different from how after
behaves in a try
/catch
, where it will be evaluated unconditionally.
These two functions follow a common Erlang pattern: building a list through recursive calls - the selective receive
doesn't really matter here. Note that this expression:
[Message | important()]
is equivalent to:
[Message] ++ important()
That is, we're creating a list whose first element is the message we received, and whose tail consists of whatever the recursive call returns. The result is that we get a list of messages received, one after another. The fact that at some point we switch to calling normal()
instead of important()
doesn't change that - we're still building the list one element at a time.
Step by step:
multiproc:important()
from the shellimportant
receives {15, high}
, we're now evaluating [high] ++ important()
important
ignores {7, low}
because it doesn't match, but receives {17, high}
which does. We're now evaluating [high] ++ [high] ++ important()
important
doesn't see any matching message and goes into the after
clause. We're now evaluating [high] ++ [high] ++ normal()
normal
receives {7, low}
, we're now evaluating [high] ++ [high] ++ [low] ++ normal()
normal
receives {1, low}
, we're now evaluating [high] ++ [high] ++ [low] ++ [low] ++ normal()
normal
doesn't see any matching message and returns []
without making a recursive call. We now have [high] ++ [high] ++ [low] ++ [low] ++ []
, which is equal to [high, high, low, low]
.