Does Haskell have For Loops?

Before I get started with today's article, I'll mention that today, April 4th is the last day to take advantage of our Spring Sale! If you subscribe today, you'll get a discount code that will get you 20% off any of our courses. This code is good until 11:59PM tonight (PST, GMT-07), so don't wait!

But now on to our new topic for the month of April: For Loops! I'll start with my least favorite quote about Haskell and for loops.

"But Haskell doesn't even have for loops!"

-- Anyone who dislikes Haskell.

This statement is often used to demonstrate just how different Haskell is from other languages. And unfortunately often, it's used to scare beginners away from trying Haskell. For loops are, in some ways, one of the most fundamental building blocks of modern programming languages. After variables, "if" statements and perhaps basic functions, they are one of the first bits of syntax you'll need to pick up on when you're learning a language like C++, Java, Javascript, Python, Rust, or Go. It's almost just muscle memory for a lot of programmers. "Oh I have to do something with every element of an iteration...", and out come the following lines:

for (int i = 0; i < my_array.size(); ++i) {
  // do something
}

// or

for (const auto& item : my_array) {
  // do something
}

So the idea that Haskell "doesn't have for loops" can make it a scary prospect to learn Haskell. How can one give up something so fundamental to programming?

However, some programmers suggest, in apparent contrast, that "bare" loops, including for loops, are a code smell. In C++ especially, there is probably an existing std::algorithm function that gives exactly the loop behavior you are looking for (e.g. std::search, or std::copy_if). And using these built-in functions will save you from some potential bugs.

A lot of these algorithms are "functional", requiring you to pass a function as an input to the algorithm. And this is the point! Haskell doesn't have the same broad, generic for syntax (although a for function does exist, as we'll explore). But it allows all the behaviors of for loops through different functional algorithms.

For the month of April, we'll explore all the common for-loop patterns and describe how to implement them in Haskell. Many of these are a simple matter of a single functional combinator. Today we'll look at two examples of this pattern: map and filter.

Consider these two patterns we could write.

  1. Given a list of integers, return a new list where each value is doubled.
  2. Given a list of integers, return a new list containing only the even values in the first list.

Here's some "bare" for loop code we could write to accomplish this in C++:

std::vector<int> myInts = {...};
std::vector<int> doubled;
std::vector<int> onlyEven;

for (int i = 0; i < myInts.size(); ++i) {
  doubled.push_back(myInts[i] * 2);
}

for (int i = 0; i < myInts.size(); ++i) {
  if (myInts[i] % 2 == 0) {
    onlyEven.push_back(myInts[i]);
  }
}

In Haskell, we don't need a for construct for these tasks. We just have the combinators map and filter, which take functions as arguments. In the most basic sense, we can treat these as operating over lists.

map :: (a -> b) -> [a] -> [b]

filter :: (a -> Bool) -> [a] -> [a]

Getting our new lists is as easy as supplying a lambda function to each of these.

myInts :: [Int]
myInts = ...

doubled :: [Int]
doubled = map (2 *) myInts

onlyEven :: [Int]
onlyEven = filter (\x -> x `mod` 2 == 0) myInts

Both of these concepts generalize to many structures though! For the idea of "mapping", we can use the fmap function over any Functor type.

fmap :: Functor f => (a -> b) -> f a -> f b

For filter, we can use any Foldable type.

filter :: Foldable t => (a -> Bool) -> t a -> t b

Most basic data structures implement these classes, so we can use these functions extremely generically!

Throughout the rest of the month we'll be talking about more ways Haskell replaces for-loops with functional constructs. Make sure to subscribe so you can stay up to date with the latest news!

Previous
Previous

Try Fold First!

Next
Next

Spring Sale ends in 4 Days!