Next lesson playing in 5 seconds

  • Overview
  • Transcript

2.3 Control Flow and Recursion

In this lesson, we’ll look at how to control the flow of your application with conditionals and recursion.

Related Links

2.3 Control Flow and Recursion

Hi, and welcome back to Get Started With Elixir. In this lesson, we are going to talk about two important concepts, controlling flow and recursion. Let's start with the simplest one, the if statement. It works like in any other language. The program will evaluate a condition. And if it returns true, it will jump into the block and execute it. There is also the corresponding else statement you're used to. In addition to that, you can also use unless to require the condition to be false. However, if statements are used less often in Elixir than in other languages. If you want to choose between multiple conditions like an if else, you would use the cond macro in Elixir. Cond evaluates a condition and will run the block for the first one that returns true. If none of them returned true, you should always include a true condition that acts as a default clause. A case macro will compare a value against provided patterns. It looks similar to the cond macro, only that its condition is always to match the value to a pattern. A good example would be a function result. Let's look at an example you will see quite often in Elixir. I am assuming there is a function do_work, of which we want to know the outcome and react appropriately. In case of a tuple beginning with ok, we want to extract the value from it. If there is an error, we want the error message. And in any other case, we log that we received an unknown response. Before we can start with recursion, we have to talk about the concept of loops. Most programming languages have for and while loops to run a piece of code a certain number of times. This doesn't exist in Elixir, because recursion, combined with pattern matching, is often a superior way to solve a problem. Now what's recursion? Recursion happens when a function calls itself. In the lesson about functional programming, we already created a recursive function to sum up two lists. Let's do a similar example to calculate the length of a list. I'm going to create a new file for this called my_list.exs, and within it, I'm creating a module of the same name. In the module, we can define a function called length, which accepts a list argument. This function doesn't actually do that much, but it initializes the counting process. We pass the list to another function, also called length, that takes 0 as its second argument. This function can be defined as a private function. The first parameter is the list and the second is the count. So, how are we going to implement this? Well, we simply go through the list and add one to the count until we hit the end. Let's change the arguments a little bit to use pattern matching. The first argument should contain a list that has a head and a tail. This means the list is not empty yet. We don't care about the head, here we can use the underscore to ignore the value and assign the value of the list's tail to a variable of the same name. In the function body, we can call the length function again, passing just the tail and increasing the count by one. Now we need a second function that expects an empty list, returning just the count, and therefore starting a chain of return statements until the value is returned by the actual length method. I'm using an alternative syntax here that makes functions with a lot of pattern matching and short bodies easier to read. Let's simulate what's happening here, when the length function is called with a list that contains 1, 2, 3, and 4. It will call the private length function with a count of 0. This function will call length again with 2, 3, and 4 and a count of 1, the next is 3 and 4 and 2, and finally 4 and 3. Then there will be an empty list with a count of 4. This resulting count will be returned to the parent call until it reaches the initial length function. Finally, let's also talk about guards. Guard clauses help with pattern matching to not only match the form of an argument, but also the contents. Let's look at a common example. Here we make a call to an api, and let's assume we get a response that either returns ok with a body or an error. As you can probably know, 400 errors in HTTP indicate a client error and 500s, a server error. With just pattern matching, we wouldn't be able to difference between those two, but we can with guard clauses. You just append the when marker, and use another matching condition. In this case, I'm using the in expression to see if the status value is in the range of the two error ranges. Guard clauses aren't only used in control flow statements, but can also be used with functions. Let's return to our example file and make sure that the length function receives a list by appending when is_list after the parameters. There is a variety of guard expressions you can use. And I've linked to this list in the lesson notes. To recap, if-statements are not so common in Elixir, cond is much more versatile. Case statements are often used to pattern match function returns and determine further action. There are no loops in Elixir, it uses recursion instead. Pattern matching can be used in case statements or functions. Guard clauses help to not only match a pattern, but also the contents of a value. In the next lesson, we are going to look at a few tools Elixir provides to manage your projects and use third-party code with it. See you there.

Back to the top