Does Elixir have for loops?
Tags: elixir, functional, beginners, python
Published: 2021-09-23 11:43:04

*Since the dawn of civilization, humanity has yearned for one thing — the ability to effortlessly repeat an action until an arbitrary condition has been satisfied.*

The for loop is a programming concept that is infinitely versatile. While it's not always the best choice in every situation, a case can be made for it in virtually any situation where a programmer needs something to occur more than once.

A new developer may be surprised, then, to Google "does elixir have for loops" and be met with the answer "Elixir doesn't have looping constructs like a for loop or a while loop" ([source](https://davidtang.io/2019-01-13-learning-how-to-loop-in-elixir-through-recursion/)).

![Screenshot of Google search results for 'does elixir have for loops'](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j1lr9w9bt203pbuoyt37.png)

## How would you write code without loops?

While recursion in many programming languages comes with the burden on high memory usage, elixir uses [tail call optimization](https://en.wikipedia.org/wiki/Tail_call) to achieve similar performance of traditional looping constructs.

Despite it being fairly simple to write your own implementation of various loops using recursion, you often won't need to. The `Enum` and `Stream` modules provide most of the functionality that you would typically want from a loop. 

*The main difference between these modules is that they are eager-loaded and lazy-loaded respectively, making Stream more well suited for very large collections. The `Stream` module will not be explored in depth in this post.*

Here's a few examples of common operations that would replace some code where you might use a for loop:

### Map

Map applies a function to every element in a list and returns the results in a list.

```elixir
numbers = [1, 2, 3, 4]
squares = Enum.map(numbers, fn x -> x * x end)
```

### Filter

Filter returns only the elements that result in `true` being returned from a given function.

```elixir
numbers = [1, 2, 3, 4]
odds = Enum.filter(list, fn x -> rem(x, 2) == 1 end)
```

### Find

Find returns the first element in a list that results in `true` being returned when passed into a given function.

```elixir
numbers = [1, 2, 3, 4]
first_even = Enum.find(list, fn x -> rem(x, 2) == 0 end)
```

*I'll be making a post soon with more in depth explanations of different array operations, so make sure to [follow me on Dev](https://dev.to/brewinstallbuzzwords) or [subscribe to my newsletter](https://mailchi.mp/89dc077154c7/brewinstallbuzzwords) so you don't miss it. For now though, see [the documentation](https://hexdocs.pm/elixir/Enum.html) for more details and other available functions.*

The above examples show situations where you might replace a for loop with a more specific implementation, but Elixir also has a construct that's even closer to a for loop...

## Loop comprehensions

These are a bit of syntactic sugar that let you do things like map or filter on an enumerable while treating it like a loop.

To square a list of number in JavaScript you might write something like this (I know there's functions that would simplify this, but this example is here to show how you might do this with a for loop):

```javascript
let list = [1, 2, 3, 4, 5];
let squares = [];

for (let i = 0; i < list.length; i++) {
	squares.push(list[i] * list[i]);
}
```

In Elixir, here's how that same logic would look as a comprehension:

```elixir
list = [1, 2, 3, 4, 5]
squares = for n <- list, do: n * n
```

These comprehensions also have an option to pass in a filter. If you wanted to do the same thing as above but only square the odd numbers, it would look something like this:

```elixir
list = [1, 2, 3, 4, 5]
squares_of_odds = for n <- list, rem(n, 2) == 1, do: n * n
```

This construct should look familiar to Python developers, who would write the same logic like this:

```python
list = [1, 2, 3, 4, 5]
squares = [x * x for x in list]

# you could also use the power operator and write it like this
squares = [x**2 for x in list]

# if you only wanted to square the odd numbers
squares_of_odds = [x**2 for x in [1, 2, 3, 4] if x % 2 == 1]
```

## Additional options

To add a bit more flexibility, Elixir loop comprehensions include the `:into` and `:reduce` options, which you can learn about [here](https://elixir-lang.org/getting-started/comprehensions.html#the-into-option).

## So, Elixir has for loops?

Not really. Although loop comprehensions look like loops, they're actually macros that rewrite the code into recursive implementations. Additionally, loop comprehensions take enumerable collections as an argument and produce an enumerable collection as a result. Loops, on the other hand, will continue to execute as long as any arbitrary condition is true.

## Further reading

- [David Tang's blog post](https://davidtang.io/2019-01-13-learning-how-to-loop-in-elixir-through-recursion/), which I referenced above, has some good examples of how to rewrite loop operations in Elixir using recursion
- [Documentation for Enum module](https://hexdocs.pm/elixir/Enum.html)
- [Documentation for Stream module](https://hexdocs.pm/elixir/1.12/Stream.html)
- [Getting started guide for Elixir comprehensions](https://elixir-lang.org/getting-started/comprehensions.html)
- [Documentation for comprehensions](https://hexdocs.pm/elixir/Kernel.SpecialForms.html#for/1)
- [Python comprehensions tutorial](https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions)

## More content

If you liked this, you might also like some of my other posts:

* [Learn Elixir with me!](https://www.brewinstallbuzzwords.com/posts/learn-elixir-with-me/)
* [Project Tours: Bread Ratio Calculator](https://www.brewinstallbuzzwords.com/posts/bread-ratio-calculator/)
* [Changing Emoji Skin Tones Programmatically](https://www.brewinstallbuzzwords.com/posts/emoji-skin-tone/)
* [I made my first svg animation!](https://dev.to/brewinstallbuzzwords/i-made-my-first-svg-animation-29j3)
* [5 tips for publishing your first npm package](https://www.brewinstallbuzzwords.com/posts/tips-for-first-npm-package/)
* [4 Hugo Beginner Mistakes](https://www.brewinstallbuzzwords.com/posts/hugo-beginner-mistakes/)