dominicletz

dominicletz

Creator of Elixir Desktop

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 :slight_smile:

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

sasajuric

Author of Elixir In Action

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

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

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

dominicletz

Creator of Elixir Desktop

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

sasajuric

Author of Elixir In Action

To make this happen, you could do something like:

Stream.repeatedly(&start_new_worker/0)
|> Enum.take(max(target - workers_online(), 0))

Where Next?

Popular in Announcing Top

KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 36654 110
New
Crowdhailer
Experimenting with this code. OK.try do user &lt;- fetch_user(1) cart &lt;- fetch_cart(1) order = checkout(cart, user) save_orde...
New
mischov
import Meeseeks.CSS html = HTTPoison.get!("https://news.ycombinator.com/").body for story &lt;- Meeseeks.all(html, css("tr.athing")) do...
New
OvermindDL1
I created a new library (rather I pulled out a couple files from my big project), it manages an operating system PID file for the BEAM. ...
New
tmbb
I’ve decided to create this topic to discuss optimization possibilities for something like Phoenix LiveView. I’ve created this topic unde...
144 10789 141
New
maltoe
Hello! Came here to announce ChromicPDF, a pet project PDF generator I’ve been working on for the past few months. Why another PDF gener...
New
MRdotB
I needed to reuse React components from my Chrome extension in my Phoenix/LiveView backend. I noticed that for Svelte/Vue, there are live...
New

Other popular topics Top

JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
New
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New

We're in Beta

About us Mission Statement