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

Last Post!

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

Where Next?

Popular in Questions Top

baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
hariharasudhan94
lets say i have a sample like a = 20; b = 10; if (a &gt; b) do {:ok, "a"} end if (a &lt; b) do {:ok, b} end if (a == b) do {:ok, "equa...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? Ecto.Repo — Ecto v3.14.0 has exampl...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
shijith.k
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
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

Other popular topics Top

electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 54006 488
New
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New

We're in Beta

About us Mission Statement