dsaghliani

dsaghliani

How can I receive all messages in the inbox and return them as a list?

I’d like to write a function that receives all messages in the inbox and returns them as a list. It should be a dead-simple task, but I can’t quite trace the recursion.

def receive_messages(timeout) do
  receive do
    data ->
      case receive_messages(timeout) do
        {:ok, next_data} ->
          {:ok, [data | next_data]}
        :empty ->
          {:ok, data}
      end
  after
    timeout ->
      IO.puts("No messages in inbox.")
      :empty
  end
end

Expected Output (If There Are Messages)

{:ok, ["Message from #1.", "Message from #2.", "Message from #3."]}.

Actual Output (If There Are Messages)

{:ok, ["Message from #1.", "Message from #2." | "Message from #3."]}.


There’s something fishy about my receive_messages/1, but I just can’t fix it. I should probably split the function into several cases, too, but I couldn’t figure out how they should be structured.

Marked As Solved

lud

lud

Also note that if you can pattern match on the empty list outside of the function. Sometimes it does not make sense to have :empty as a special case.

So that could be:

  def receive_messages(timeout, acc \\ []) do
    receive do
      msg -> receive_messages(timeout, [msg | acc])
    after
      timeout -> {:ok, :lists.reverse(acc)}
    end
  end

And it just returns {:ok, []} if no messages were received.

And now, since it cannot return {:error, _} or :empty I would just remove the tuple and return a list:

  def receive_messages(timeout, acc \\ []) do
    receive do
      msg -> receive_messages(timeout, [msg | acc])
    after
      timeout -> :lists.reverse(acc)
    end
  end

Also Liked

antoine-duchenet

antoine-duchenet

Hi,

Your :empty case should probably return a list as data :

def receive_messages(timeout) do
  receive do
    data ->
      case receive_messages(timeout) do
        {:ok, next_data} ->
          {:ok, [data | next_data]}
        :empty ->
          {:ok, [data]} # here
      end
  after
    timeout ->
      IO.puts("No messages in inbox.")
      :empty
  end
end

Otherwise, your last next_data will not be a list, creating "Message from #2." | "Message from #3." by concatenation :

iex(1)> ["2" | "3"]        
["2" | "3"]
iex(2)> ["2" | ["3"]]
["2", "3"]
LostKobrakai

LostKobrakai

Nope. I’ll try to pop a single item off the enumerable and if there is one returns false otherwise the enumerable is empty. It doesn’t enumerate everything.

Even if it’s constant time it’s still more work though:

map = for i <- 1..5000, into: %{}, do: {i, System.unique_integer()}
Benchee.run(%{
  "map_size" => fn -> map_size(map) > 0 end,
  "pattern" => fn -> map != %{} end
})
Operating System: Linux
CPU Information: AMD Ryzen 3 3200G with Radeon Vega Graphics
Number of Available Cores: 4
Available memory: 5.80 GB
Elixir 1.13.2
Erlang 24.1.7

Benchmark suite executing with the following configuration:
warmup: 2 s
time: 5 s
memory time: 0 ns
reduction time: 0 ns
parallel: 1
inputs: none specified
Estimated total run time: 14 s

Benchmarking map_size ...
Benchmarking pattern ...

Name               ips        average  deviation         median         99th %
pattern         1.45 M        0.69 μs  ±5168.36%        0.47 μs        0.96 μs
map_size        0.93 M        1.07 μs  ±2783.37%        0.77 μs        1.65 μs

Comparison: 
pattern         1.45 M
map_size        0.93 M - 1.56x slower +0.38 μs
lud

lud

I would go with tail recursion on this one:

  def receive_messages(timeout, acc \\ []) do
    receive do
      msg -> receive_messages(timeout, [msg | acc])
    after
      timeout ->
        case acc do
          [] -> :empty
          _ -> {:ok, :lists.reverse(acc)}
        end
    end
  end
dsaghliani

dsaghliani

Yes, that’s a lot simpler! I was having trouble following my own implementation.

I was planning to replace my use of a list—which was simpler to prototype—with a map, and I had an easier time with your code. Thank you.

lud

lud

If your function returns a map then you can use if map_size(map) > 0

In my examples, the function returns a list, so you can do:

case receive_messages(100) do
  [] -> :ok
  msgs -> do_stuff(msgs)
end

Now if you call Enum.each/2 or something like that on your list of messages you do not care wether the list is empty or not.

Where Next?

Popular in Questions Top

srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call th...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
wernerlaude
In AR this is so simple @articles = current_user.articles How to do in Ecto? def index(conn, _params) do current_user = conn.assig...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

Other popular topics Top

senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
quazar
How to set Jason to encode all fields in ecto schema, I don’t care about security and implementing only is taking long list of attributes...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
beno
I will often find my self writing things similar to: case some_value do nil -&gt; something() "" -&gt; something() _ -&gt; someth...
New

We're in Beta

About us Mission Statement