Selective receiving in Erlang
Asked Answered
C

3

11

So I've started learning Erlang and I'm a little confused with this chunk of code.

 -module(prior).
 -compile(export_all).


    important() ->
      receive
    { Priority, Msg } when Priority > 10 ->
      [Msg | important()]
  after 0 ->
     normal()
  end.

normal() ->
  receive
    { _, Msg } -> 
      [Msg | normal()]
  after 0 ->
      []
  end.

I am calling the code using.

    10> self() ! {15, high}, self() ! {7, low}, self() ! {1, low}, self() ! {17, high}.
    {17,high}
    11> prior:important(). 
        [high,high,low,low]

I understand that this code will go through all the high priority messages first and then the low priority ones. I'm confused as to how the return value is[high,high,low,low], since I don't see where they are concatenated together.

Card answered 10/6, 2012 at 20:2 Comment(1)
Not concatenated, consed. concatenation is when you have two lists, L1 and L2 and concatenate them: L1 ++ L2. Consing is when you have an element E and a list L and then form the extended list [E | L].Hibernate
O
18

How the final return value is constructed...

When [Msg | important()] is being returned for the first time, the form of the final return value is determined. The only concern is, we don't know all the details of the final return value yet. So the important() in the [Msg | important()] will continue being evaluated. The following is an illustration of how the final return value [high,high,low,low] is constructed.

[high | important(                      )]  <---- Defines the final form
        ---------------------------------
        [high | important(             )]   <---- Adds more details
                ------------------------
                normal(                )    <---- Adds more details
                ------------------------
                [low | normal(        )]    <---- Adds more details
                       ----------------
                       [low | normal()]     <---- Adds more details
                              --------
                              [      ]      <---- Adds more details
------------------------------------------
[high | [high | [low | [low | []]]]]
[high,high,low,low]                         <---- The final return value

How the code works...

In function important/0, after 0 simply means "I don't wait for messages to come" -- if there is any message in my mailbox, I will look at it; if there isn't any, I will keep going (execute normal()) rather than waiting there. In the mailbox, there are {15, high}, {7, low}, {1, low}, {17, high} sitting there already. In Erlang, the messages in the mailbox are Not queued in a first-come-first-serve order. The receive clause can be picky. It scans through all the messages in the mailbox and "picks" the ones it desires. In our case, {15, high} and {17, high} get picked first according to {Priority, Msg} when Priority > 10. After that, the function normal/0 takes over. And {7, low}, {1, low} get processed (consed) in order. Finally, we got [high,high,low,low].

A modified version that reveals the processing order...

We can modify the code a little bit in order to make the processing (consing) order more explicit:

-module(prior).
-compile(export_all).

important() ->
    receive
    {Priority, Msg} when Priority > 10 ->
        [{Priority, Msg} | important()] % <---- Edited
    after 0 ->
    normal()
    end.

normal() ->
    receive
    {Priority, Msg} -> % <---- Edited
        [{Priority, Msg} | normal()] % <---- Edited
    after 0 ->
        []
    end.

Run it in the shell:

4> c(prior).
{ok, prior}
5> self() ! {15, high}, self() ! {7, low}, self() ! {1, low}, self() ! {17, high}.
{17,high}
6> prior:important().
[{15,high},{17,high},{7,low},{1,low}]
Oxytocin answered 11/6, 2012 at 0:46 Comment(2)
I wrote the code the OP asked about (I do believe this is my priority receive in Learn You Some Erlang) and I approve of this answer.Cagle
I am a little bit confused. Do you mean that the messages are not queued in a first-come-first-serve order even if selective receive is not used? Are they processed more or less randomly?Camacho
G
4

they are concated here

[Msg | important()]

this important() is a function so it has a return value, while you run this in REPL he will print return value from function. This value is effect of [Head | Tail] list building from import()

important() here is a regular function :)

Is it helpful ?

Gyrfalcon answered 10/6, 2012 at 20:9 Comment(2)
Thanks for the quick reply. So even the low priority messages received from the normal() call in after are concatenated to the high priority list in the [Msg | important()] clause?Card
everything. Also probably it will flood your ram if you would have like loads of messages so you should covert it into tail recursive function. I don't know what are you trying to build because it in general should be a gen_server with a priority que as inside data structure. You are trying to do it inside a process message que and this thing is limited so it is bad idea in general.Gyrfalcon
I
2

All Erlang functions always return a value. The function important/0 will receive a high priority message and then call itself recursively in the expression [Msg | important()] which builds a list containing the latest Msg and all the other messages which important/0 will receive. It is this list which is returned from important/0. When there are no more high priority messages then important/0 will instead call normal/0 to read all the remaining messages. The messages which normal/0 reads it will return as a list in the same way important/0. This will be returned to important/0 which will then return it in the same list as it has returned its messages.

Note that once normal/0 has been called there will be no special handling of high priority messages as important/0 is never called again. Also important/0 will really only process the high priority messages already in the queue as once it can not find any more then it calls normal/0.

The timeout value 0 is special in that it times out immediately but guarantees to first search the whole message queue for matching messages.

Irresponsive answered 11/6, 2012 at 0:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.