Elixir implementation of algorithm

Hello, I was wondering as there is no while loop in elixir, how would that look like written in Elixir:
while exists (s, m, sn) ∈ pending such that sn = next[s] do
next[s] := next[s]+1;
pending := pending \ {(s, m, sn)};

I do not really get what that algorithm does. To me it seems like you have some problem with transcription of the original one.

1 Like

pending is a set of processes, next is an Array

Ahh, ok. Now I get. I can take a look later when I will be near computer

Generally you would turn the while loop into an Enum.reduce and the variables that you would have mutated get passed as part of the accumulator.

1 Like

This appears to be a fragment from Algorithm 3.12 in “Introduction to Reliable and Secure Distributed Programming”.

Folks will be able to answer this question a lot more effectively if they have that context.

In that algorithm, pending is a set of tuples {sender, message, sequence_number} while next is a map from sender to the next sequence_number expected from that sender.

The while loop finds {sender, message_to_deliver, sequence_number} values in pending, and does three things:

  • increments next for sender
  • removes the tuple from pending
  • delivers message_to_deliver from sender
1 Like

Something like this would be my attempt:

pending = [
  {1, "hello1", 2},
  {1, "hello2", 1},
  {2, "hello3", 1},
  {2, "hello4", 2},
  {2, "hello5", 4}
]

next = %{
  1 => 1,
  2 => 1
}

pending_transformed =
  for {sender, message, sn} <- pending,
      into: %{},
      do: {{sender, sn}, message}

{pending_transformed, next}
|> Stream.unfold(fn
  nil ->
    nil

  {pending_transformed, next} ->
    result =
      pending_transformed
      |> Map.keys
      |> Enum.find(fn {sender, sn} -> next[sender] == sn end)

    case result do
      {sender, sn} = key ->
        {message, pending_transformed} = Map.pop!(pending_transformed, key)
        next = Map.put(next, sender, sn + 1)
        {{:send, {sender, message, sn}}, {pending_transformed, next}}

      nil ->
        {{:done, {pending_transformed, next}}, nil}
    end
end)
|> Enum.reduce_while([], fn
  {:send, msg}, acc ->
    IO.inspect(msg)
    {:cont, acc}

  {:done, rest}, _ ->
    {:halt, rest}
end)

# Output
{1, "hello2", 1}
{1, "hello1", 2}
{2, "hello3", 1}
{2, "hello4", 2}

# Result
{%{{2, 4} => "hello5"}, %{1 => 3, 2 => 3}}
1 Like

Do you by any chance have that algorithm in code version ?