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: while | Hex
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
Last Post!
sasajuric
To make this happen, you could do something like:
Stream.repeatedly(&start_new_worker/0)
|> Enum.take(max(target - workers_online(), 0))
Popular in Announcing
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #hex
- #security









