Using While Loops in Elixir

May 4 · 4 min read

Most people would tell you not to use a while loop in Elixir. And most people are right. Since data is immutable, they generally won’t do you much good. But I am not most people.

A common while loop looks something like this:

This wouldn’t quite work in Elixir. The value of x can't change, so depending on the value of x, the loop would either continue forever, or finish immediately. Not so helpful is it? "But Wait!" you proudly shout, "We can just reassign the value of x and loop to our hearts content". But thats not really a loop in the traditional sense. Using a reassigned variable would be the equivalent of calling a function recursively, which incidentally, is how you "loop" in Elixir.

And voilà, you have iterative code. Since true while loops require mutable data, it's understandable why they weren't included in the language. But I've always been a rebel at heart. Who is José Valim to tell me what I can and can't do? While loops are my God given right!

Dealing with State

Here’s the hard truth. State happens. To prove it, I’ve written a stoplight server.

This is a simple GenServer that updates it’s internal traffic light state every millisecond (I’m told this is how it works in Los Angeles). We’re also given a check_light function to view the state at any time. Now, if I want to consume this information, I'll need to interact with a stateful world. I've also written a bit of self driving car software that interacts with the stoplight server. Like Elon, I've decided to open source my code for an autonomous vehicle. You're welcome mankind.

This will repeatedly check the stoplight to see if it’s green. If it is not green, it will tell you to wait (yes, even on yellow lights you freewheelin’ maniac). Simple stuff. The kind of stuff that seems perfect for a while loop, so I’m going to use one. Let’s try writing us an old fashioned while function.

We created a function that will check a given value, and if true, will perform the given function recursively until that value isn’t true anymore.

But running this against our stateful server will highlight a problem.

Our loop will either continue forever, or finish immediately. The stateful expression is computed before being passed into the function. The check_light function is only called once on the initial while invocation. If we want to re-evaluate the predicate on every iteration, we'll have to wrap it inside yet another function. But I think we're starting to stray too far from the while loop I want. Lets crank the volume up to 11 and utilize some macros.

A Macro While

Now most people will tell you not to use macros unless absolutely necessary. And I’m going to have to agree with most people here. But this is absolutely necessary, nothing gets between me and my while loops. Here’s while written as a macro.

This does some pretty gnarly stuff. We define an anonymous recursive function, which accepts a function that will be called if the predicate evaluates to true. After defining this function, we pass it into itself. This is just a trick to create a recursive function while avoiding the def keyword, allowing us to call our while outside of a module definition.

Despite all this ugliness, we actually get a pretty slick interface.

This is almost perfect, but the syntax isn’t quite right. While.while? This ain’t the while while west. I just want a plain and simple, no frills while loop. I also want the title of this post to be clever, so we're going to use the __using__ keyword. When a module uses use with another module, it will immediately call the __using__ callback from the module being used. We're going to use this to define a macro in the Car module. Have I used the word "use" enough in this paragraph? I need the SEO points.

And there you have it! When the Car module is compiled, it will call the __using__ macro, which defines it's own while macro inside of Car. And now we can call while straight from our Car module.

Closing Thoughts

Even after all this, we’re still using recursion. We just gave it an unnecessary paint job. You don’t need while loops. Use recursion, it's cleaner and more fun.


Originally published at https://codingwithalchemy.com on May 4, 2019.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade