# Your first Elixir Project (Part 2)

**Table of contents:**

## Before we get started…

This is part 2 of a series for building your first project in Elixir. If you’re brand new to Elixir, start with my overview of the language.

This post assumes you’ve already completed parts 0 and 1 of this series already.

## Now, where were we?

At the end of Part 1, we had written and tested a few functions for converting between units of measure.

We had also added some error handling for invalid input and wrote some tests for that too.

In this part, we’ll be re-writing some of our existing code to utilize type guards. We’ll also be writing some new functions and using pipes.

## Our existing code

If you’ve been following along from the beginning, you should have a `kilograms_to_grams/1`

function that looks like this:

```
def kilograms_to_grams(x) do
if is_number(x) do
x * 1000
else
{:error, "invalid input"}
end
end
```

There’s nothing wrong with this as-is, but Elixir has some tools to help us eliminate some of this nesting.

Using guards, we can limit our function to only run when the input is valid. Here’s what that would look like:

```
def kilograms_to_grams(x) when is_number(x) and x >= 0 do
x * 1000
end
```

Guards start with `when`

and are followed by a boolean expression. Only when the expression is `true`

will the function execute.

To test this, let’s see what happens when we run our tests with `mix test`

and the invalid input tests are reached:

```
1) test kilograms to grams handles invalid input (UnitConverterTest)
test/unit_converter_test.exs:15
** (FunctionClauseError) no function clause matching in UnitConverter.kilograms_to_grams/1
```

From this message, we can see that it never reached the inside of the function. Instead, we get an error that there was no matching function clause.

## Handling invalid input

To make sure we still handle invalid input, we need to define another function with the same name and arity, but without the guards:

```
def kilograms_to_grams(x) when is_number(x) and x >= 0 do
x * 1000
end
def kilograms_to_grams(x) do
{:error, "invalid input"}
end
```

When a call to `kilograms_to_grams/1`

is made, it will call our first function if the input is a number and greater than or equal to zero. Otherwise, it will default to our error function.

Let’s make sure we did this right and run our tests again:

```
....
Finished in 0.1 seconds
1 doctest, 3 tests, 0 failures
```

Great, we still pass our tests!

But a closer look will reveal that we also get a syntax warning:

`warning: variable "x" is unused (if the variable is not meant to be used, prefix it with an underscore)`

## Ignoring function parameters

We need to accept a parameter in order for it to be a fallback for our guarded version of `kilograms_to_grams/1`

. Removing the parameter would cause us to instead have `kilograms_to_grams/0`

, which would break our intended control flow.

But we also don’t need to do anything with this parameter when it doesn’t meet the criteria of our guard.

Luckily, the warning tells us how to fix the issue. Prefixing a parameter name with an underscore will tell the compiler that we need to accept the parameter, but that it won’t be used within the function.

```
def kilograms_to_grams(x) when is_number(x) and x >= 0 do
x * 1000
end
def kilograms_to_grams(_x) do
{:error, "invalid input"}
end
```

If we run the tests again now, the warning should be resolved.

## Adding more conversions

Now that we have this conversion working, let’s follow the same pattern for converting ounces to grams and pounds to ounces. Because this code is essentially the same as what we’re using in `kilograms_to_grams/1`

but with different conversion factors, feel free to just copy and paste these tests and functions.

Add these tests to `unit_converter_test.exs`

:

```
test "converts ounces to grams" do
assert Float.floor(UnitConverter.ounces_to_grams(10), 4) == 283.4951
assert UnitConverter.ounces_to_grams(0) == 0
assert Float.floor(UnitConverter.ounces_to_grams(1.5), 4) == 42.5242
end
test "ounces to grams handles invalid input" do
assert UnitConverter.ounces_to_grams("hello there") == {:error, "invalid input"}
assert UnitConverter.ounces_to_grams(-1) == {:error, "invalid input"}
assert UnitConverter.ounces_to_grams([1, 2, 3]) == {:error, "invalid input"}
assert UnitConverter.ounces_to_grams(:invalid) == {:error, "invalid input"}
end
test "converts pounds to ounces" do
assert UnitConverter.pounds_to_ounces(10) == 160
assert UnitConverter.pounds_to_ounces(0) == 0
assert UnitConverter.pounds_to_ounces(1.5) == 24
end
test "pounds to ounces handles invalid input" do
assert UnitConverter.pounds_to_ounces("hello there") == {:error, "invalid input"}
assert UnitConverter.pounds_to_ounces(-1) == {:error, "invalid input"}
assert UnitConverter.pounds_to_ounces([1, 2, 3]) == {:error, "invalid input"}
assert UnitConverter.pounds_to_ounces(:invalid) == {:error, "invalid input"}
end
```

Note that these tests make use of `[Float.floor/2](https://hexdocs.pm/elixir/1.13/Float.html#floor/2)`

to round the results of `ounces_to_grams/1`

for testing.

Add these functions to `unit_converter.ex`

:

```
def ounces_to_grams(x) when is_number(x) and x >= 0 do
x * 28.34952
end
def ounces_to_grams(_x) do
{:error, "invalid input"}
end
def pounds_to_ounces(x) when is_number(x) and x>= 0 do
x * 16
end
def pounds_to_ounces(_x) do
{:error, "invalid input"}
end
```

## Changing things up a bit

Next we’re going to write a function to convert pounds to grams. But instead of looking up the conversion factor and plugging it into a function, we’re going to leverage our existing functions `pounds_to_ounces/1`

and `ounces_to_grams/1`

.

We’ll start the same way by writing some simple tests:

```
test "converts pounds to grams" do
assert Float.floor(UnitConverter.pounds_to_grams(10), 2) == 4535.92
assert UnitConverter.pounds_to_grams(0) == 0
assert Float.floor(UnitConverter.pounds_to_grams(1.5), 2) == 680.38
end
test "pounds to grams handles invalid input" do
assert UnitConverter.pounds_to_ounces("hello there") == {:error, "invalid input"}
assert UnitConverter.pounds_to_ounces(-1) == {:error, "invalid input"}
assert UnitConverter.pounds_to_ounces([1, 2, 3]) == {:error, "invalid input"}
assert UnitConverter.pounds_to_ounces(:invalid) == {:error, "invalid input"}
end
```

Then we’ll write our function utilizing the same techniques we’ve used above:

```
def pounds_to_grams(x) when is_number(x) and x >= 0 do
ounces = pounds_to_ounces(x)
ounces_to_grams(ounces)
end
def pounds_to_grams(_x) do
{:error, "invalid input"}
end
```

The only difference here is that instead of multiplying or dividing our input value, we:

- Pass the input into
`pounds_to_ounces/1`

- Store the result in a variable named
`ounces`

- Pass
`ounces`

into`ounces_to_grams/1`

Alternatively, we could nest the function calls:

```
def pounds_to_grams(x) when is_number(x) and x >= 0 do
ounces_to_grams(pounds_to_ounces(x))
end
```

## But there’s a better way

The above code is fine, but Elixir has an operator that lets you avoid nested function calls or creating variables that are only used once.

## Pipes

The pipe operator, `|>`

, works by passing the return value of one call as the first parameter in the next call.

Here’s the above function rewritten using pipes:

```
def pounds_to_grams(x) when is_number(x) and x >= 0 do
pounds_to_ounces(x)
|> ounces_to_grams()
end
```

First, `pounds_to_ounces(x)`

is evaluated. Then, the `|>`

operator passes the return value into the first (and only) parameter of `ounces_to_grams/1`

.

While this concept may feel foreign at first, it’s really helpful for when you need to make sequential calls to modify some data.

## Adding pipes to our tests

We can also eliminate some function call nesting in our tests for `ounces_to_grams/1`

and `pounds_to_grams/1`

.

```
test "converts ounces to grams" do
assert UnitConverter.ounces_to_grams(10) |> Float.floor(4) == 283.4951
assert UnitConverter.ounces_to_grams(0) == 0
assert UnitConverter.ounces_to_grams(1.5) |> Float.floor(4) == 42.5242
end
```

```
test "converts pounds to grams" do
assert UnitConverter.pounds_to_grams(10) |> Float.floor(2) == 4535.92
assert UnitConverter.pounds_to_grams(0) == 0
assert UnitConverter.pounds_to_grams(1.5) |> Float.floor(2) == 680.38
end
```

## You did it!

Now you know how to use type guards and pipes in Elixir. You’ve also written a small module that you can add more of your own functions to.

To make sure you don’t miss the next part of this series or any of my other Elixir content, subscribe to my monthly newsletter, follow me on Dev, or follow me on Twitter.

If you liked this post, click here to subscribe to my mailing list!