dominicletz
While - Elixir macro for ruby traditional while - looking for feedback
Hi Guys,
one more time I had this need for a simple while loop and so I played with this macro idea: https://hex.pm/packages/while
While on globals
import While
ref = :counters.new(1, [:atomics])
while :counters.get(ref 1) < 10 do
:counters.add(ref 1, 1)
end
IO.puts("Current value is #{:counters.get(ref, 1)}")
Now this only works when you’re working with globals, references or pids, which in fact in my case is useful sometimes. But to make it more useful in local scopes I created a more reduce like shortcut:
While on locals
When providing an additional parameter, this is interpreted as a variable name and imported into the scope of the while expression and the while body.
import While
cnt = 1
cnt = while cnt, cnt < 10 do
cnt + 1
end
IO.puts("Current value is #{cnt}")
Explanation:
The while/3 binds the variable name cnt to both the expression cnt < 10 and to the body. The body result (last line of body) always becomes the new value for the bound variable, like in a Enum.reduce.
cnt - 1
Danger Area
To automate the assignment, there is another variant while_with that will automagically assign to the original variable. Consensus from this discussion seems to be: Don’t use this variant
import While
cnt = 1
while_with cnt, cnt < 10 do
cnt + 1
end
IO.puts("Current value is #{cnt}")
Explanation:
The while_with works exactly like while/3 but the final value, will be assigned to the bound variable of the outer scope, so that: IO.puts("Current value is #{cnt}") will print Current value is 10
Questions
The name while_with might be confusing, should this just be the same name, but a two parameter variant of while , or should it be called reduce_while or something? Curious about the communities thoughts here and whether this kind of macro has any reason to exist at all in the first place 
Installation
While can be installed by adding while to your list of dependencies in mix.exs:
def deps do
[
{:while, "~> 0.2.1"}
]
end
Most Liked
sasajuric
You don’t need to write an explicit recursive function. The same can be achieved with e.g.:
Stream.repeatedly(fn -> worker_online() == target end)
|> Enum.find(& &1)
benwilson512
Can you elaborate on these concrete use cases? I really can’t think of any situation where I wouldn’t prefer a recursive function, or the use of Enum.
cnt = 1
while_with cnt, cnt < 10 do
cnt + 1
end
IO.puts("Current value is #{cnt}")
The hidden rebinding here is particularly alarming since it changes core expectations people have about how Elixir code works. It’s also presumably inconsistent right? What if you do:
i = 0
j = 0
while_with i, i < 10 do
i + 1
j + 1
end
Is i incremented to 10, but j not?
dominicletz
Thanks all for the feedback. I’ve removed while_with from the examples and marked it deprecated - because the hidden assignment indeed seems too dangerous.
For those who still want to use while the way to go is the while/3 macro that returns the value, so the assignment is visible in the code:
cnt = 1
cnt = while cnt, cnt < 10 do
cnt + 1
end
sasajuric
To make this happen, you could do something like:
Stream.repeatedly(&start_new_worker/0)
|> Enum.take(max(target - workers_online(), 0))
LostKobrakai
There’s already Enum.reduce_while, so I’m not sure how good of a name this one would be.







