dominicletz
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
Trending in Announcing
Other Trending 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
- #blog-post
- #phoenix_html
- #iex
- #graphql
- #ai
- #genstage
- #elixirconf-us
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #security
- #hex










First 10 of 14 Posts
LostKobrakai
There’s already
Enum.reduce_while, so I’m not sure how good of a name this one would be.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.
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:
Is
iincremented to 10, butjnot?mischov
I don’t think this is a great idea.
There are more language appropriate ways of approaching the problem (see: Write while loop equivalent in elixir - #10 by peerreynders), and using this instead of them would actually add complexity for most Elixir programmers.
benwilson512
It’s also worth noting that
fortakes areduceoption now:dominicletz
In my case I’m only using the global version of this while loop, and I use them in tests to start processes and wait for them to come up:
I just prefer being able to write this in-line in the function that needs it than externalizing this every time into a recursive function, especially when it’s not reused:
That’s my reason to create it: Laziness of creating a recursive function for each while use case.
Totally agreed, the hidden rebinding is alarming. That’s why I added the _with postfix instead of just overloading the
whilemacro with two parameters for this. Maybe a name likewhile_bindwould indicate that binding easier. First I looked ati = while_with i, i < 10 do: i + 1but felt it’s just oneitoo much…Just for completeness the two, or more variable version would look like this:
benwilson512
Normally I’d expect something like this for starting workers in tests:
Your version seems to indicate that it does some kind of polling to see if workers are online, but a more idiomatic solution would be to just block until you receive a message indicating that the worker is online.
dominicletz
Yeah, it just appeared to me that a ‘conceptual while’ is basically a Enum.reduce_while without an enum, (or it could be at least):
Then you could use it as a normal function:
The presented
whileis just a shorthand / syntactic sugar for this to turn this into:dominicletz
Thanks all for the feedback. I’ve removed
while_withfrom the examples and marked it deprecated - because the hidden assignment indeed seems too dangerous.For those who still want to use
whilethe way to go is thewhile/3macro that returns the value, so the assignment is visible in the code:sasajuric
You don’t need to write an explicit recursive function. The same can be achieved with e.g.:
dominicletz
This is a pretty awesome solution @sasajuric thanks for that - I didn’t think of this combination. It’s not calling start_new_worker() but I guess it would do it like this:
While this looks correct I personally find that the while construct much better transports the meaning, at the same effect.
Cheers