Joy of Elixir

8. Built-in functions: strings, input and output

In this chapter we'll go through some of the built-in functions that Elixir has. We'll be focussing specifically on strings, input and output. Elixir has some very handy functions already available to use and we'll see just a small taster of that here.

Working with strings

We'll start by looking at the functions to work with strings. Strings are where we started back in Chapter 1, so it only makes sense to start with them here, too.

Reversing a string

Have you ever wanted to reverse a sentence, but didn't want to type all the different characters yourself? Elixir has a handy function for this called String.reverse. Here it is in action:

iex> String.reverse("reverse this")
"siht esrever"

Roberto elicits a noticeable "wooooowww, that's so cool" at this. "What else does Elixir have?", he asks quickly. We'll get to that, Roberto. Let's take the time now to understand what's happening here first.

The functions that Elixir provides are separated into something akin to kitchen drawers or toolboxes, called modules. Whereas in your top kitchen drawer you might have forks, knives, sporks, and spoons (like every sensible person's kitchen does), and in another you might have measuring cups, and in another tea towels, in Elixir the functions to work with the different kinds of data are separated into different modules. This makes finding functions to work with particular kinds of data in Elixir very easy.

Here, we're using the reverse function from the String module ("drawer" / "toolbox"). We know that this is a module because its first letter is upper-case. We're passing this String.reverse function one argument, which is a string "reverse this". This function takes the string and flips it around, producing the reversed string: "siht esrever".

Note here how we don't need to put a dot between the function name and its arguments, like we had to do with the functions we defined ourselves. You don't need to do this when you're running a function from a module. You only need the dot if you've defined the function and assigned it to a variable. For instance, with our old greeting function:

iex> greeting = fn (place) -> "Hello, #{place}!" end
#Function<6.52032458/1 in :erl_eval.expr/5>
greeting.("world")

When calling the String.reverse function, Elixir knows that it's a function because of that String. prefix. We don't need a dot right after the function name:

iex> String.reverse("reverse this")
"siht esrever"

Splitting a string

What about if we wanted to split a string into its individual words? For that, we can use the split function:

iex> String.split("split my string into pieces")
["split", "my", "string", "into", "pieces"]

We now have a list of the words in the string. We'll see what we could do with such a list a little later in the chapter. For now, let's look at what else is in this String module.

Making all the letters of a string uppercase

What about if we wanted to make the computer turn a string into its shouty variant? We can use upcase for this:

iex> String.upcase("not so quiet any more")
"NOT SO QUIET ANY MORE"

Making all the letters of a string lowercase

At the opposite end of that particular spectrum, there is downcase:

iex> String.downcase("LOUD TO QUIET")
"loud to quiet"

So as you can see, the String module has some helpful functions that can help us whenever we need to split a string, turn it all into upper / lower ("down") case. There's plenty more functions in the String module, and we'll see some of those in due time.

Let's take a look at working with something brand new that we have never worked with before: input and output.

Input and output

Input and output are two fundamental things that you'll work with while programming. Programming is all about taking some data as an input and turning it into some form of an output. We've seen this multiple times already with the functions we've defined and used throughout this book. For instance, in that String.downcase function just above, the string "LOUD TO QUIET" is the input, and the "loud to quiet" generated by the method is the output.

What we'll cover in this section is getting some input from a different source: a new prompt. We'll prompt the user for their name and then we will use whatever they enter to output a message containing that input.

Making our own prompts

Let's say that we wanted to prompt people for their names and we wanted to prompt them in a way that meant that they didn't have to read Joy of Elixir to understand that strings had to be wrapped in double quotes and that they had to enter their input into an iex prompt.

Fortunately for us, Elixir has a module that provides us a function to do just this. That module is called IO (Input / Output) and the function is called gets. The name gets means "get string" and it will allow us to do exactly that. Let's see this function in practice:

iex> name = IO.gets "What is your name?"
  What is your name?

"Hey what happened to our iex> prompt?", Roberto asks. Good question! We're using gets and passing it a string. This string then becomes a new prompt. This prompt is asking us for our name. Let's type in our name and press enter:

iex> name = IO.gets "What is your name?"
What is your name? The Reader
"The Reader\n"

Ok, so there's some output here. But what does it mean? If we check our name variable's contents we'll see that it contains this "The Reader\n" string.

iex> name
"The Reader\n"

Roberto continues asking questions: "What's that \n on the end?". That is a new line character and tells the computer that we pressed enter. While the IO.gets function stopped prompting us after we pressed enter, it still kept the enter in case we wanted it too. In this particular case we don't really want that character. We can get rid of it by using another function from the String module, called trim.

iex> name = String.trim(name)
"The Reader"

That's much better! Now we have our name with that pesky new line character suffixed. What String.trim does is remove all the extra spacing from the end of a string, giving us just the important parts.

Taking input and making it output

We've now got some input, but what's the point of taking input if you're not going to do anything with it? So let's do something with it! What we'll do with this input is to output a greeting message.

Lets deviate here from using the iex prompt and instead write our code inside one of those Elixir Script (.exs) files we mentioned back at the end of Chapter 5. Let's call this file greet.exs and put this content inside of it:

name = IO.gets "What is your name? "
age = IO.gets "And what is your age? "
IO.puts "Hello, #{String.trim(name)}! You're #{String.trim(age)}? That's so old!"

Well that's a bit sneaky of that IO.puts to just appear out of nowhere! Just like gets means "get string", puts means "put string". This function will generate some output when our script runs. If we didn't have this IO.puts here, our program would only take input, and it would not generate any output.

In this function we're interpolating the output of the String.trim function twice. Remember: we're doing this to remove the new line character (\n) from the result of the IO.gets calls.

There's some more new syntax that we've never seen before either. We've seen that we could interpolate variables into strings, but we've never seen that we could call functions while interpolating too. It's absolutely something you can do in Elixir. When interpolating inside a string you can put any code inside the interpolation brackets (#{}) — but as a general rule-of-thumb it's good to keep this interpolated code as short and simple as possible.

Let's run our greet.exs script now. First, we'll need to stop our iex prompt, which we can do by pressing Ctrl+C twice. Then we can run the script with this command:

elixir greet.exs

Here's what we'll see initially:

What is your name?

The script is prompting us for our name and it is doing that because the first line of code in that script is running the IO.gets function. Let's enter our name again and press enter.

What is your name? Reader
And what is your age?

This little script is now prompting us for our age. This is because the second line is calling another IO.gets. Let's enter our age and then press enter again,

What is your name? Reader
And what is your age? 30ish
Hello, Reader! You're 30ish? That's so old!

Our script gets to the third and final line, where it runs the IO.puts function and outputs its little teasing message. Apparently being 30ish is old! Kids these days have no respect.

This is just a small example of what we can do with IO.gets and IO.puts. We could use any number of IO.gets and IO.puts function calls to build up a program that took user input and generated some output from it.

Exercises

  • Make a program that generates a very short story. Get it to take some input of a place, an action and an object and combine all three into a little sentence.
  • Ponder on what happens when you remove the IO.puts from the beginning of Line 3 in greet.exs and then run the program with elixir greet.exs. Think about how this would be different if you put that code into an iex prompt.

We've done a lot of work with strings so far in this chapter. Let's look at lists and maps again in the next chapter and the built-in functions that we can use with them.