Coding Artist
Published in

Coding Artist

Full-Stack React With Phoenix (Chapter 2 | Learning the Basics of Elixir)

Previous Chapters

Chapter 1 | Why Bother?

Scope of This Chapter

As I mentioned in the previous chapter, learning Phoenix is awesome because we can learn Elixir (the language it is built off of) in the process. However, this also means we have to put in a bit more effort before we dive into Phoenix to understand all the fundamentals of this new programming language.

Elixir is a functional programming language at its core which has some awesome features to pack a lot of punch in shorter amounts of code.

In this chapter, we will be following the main structure and types of examples as Elixir School which is a great resource for referencing the basics of Elixir in bite-sized lessons. Instead of reinventing the wheel, this chapter will be like a practical commentary on this resource.

Not only do I hope that this chapter is a practical guide to learn Elixir, but I also hope you see that Elixir is a lot of fun!

Let’s get started.

Before We Begin

Make sure to install Elixir by following the official instructions.

You can check to make sure you have Elixir installed properly by running the following in command line:

elixir -v

If it is installed correctly, we can run Elixir code in an interactive shell which you should now fire up by running:

iex

Another note: I was going to cover the question of “What is functional programming?” since Elixir is a functional language. I am just going to punt to this great article by Eric Elliot if you are unfamiliar.

Data Types

Here is a comparison between the basic Elixir data types and the JavaScript data types:

Let’s use the interactive mode in command line to test out go over these basic Elixir data types.

Integers

Integers are whole numbers. In Elixir, they can be negative or positive:

> 2 + 2
4
> -2 + 2
0

Floats

Floats are numbers with at least one decimal place:

> 2.0 + 3.4
5.4

They can also be expressed in exponential form:

> 1.0e-9
1.0e-9

Think scientific notation from your high school math class:

Basic Arithmetic

Now that we now our two number types, let’s try some example of basic arithmetic:

> 2 + 2
4
> 2.0 + 4
6.0
> 2 - 4
-2
> 2.0 - 4
-2.0
> 2 * 3
6
> 2 * 4.0
8.0
> 2 / 2
2.0
> 2/1.1
1.818181...

In the first example, we do simple addition between two integers and the result is an integer. However, the next example shows us that a float plus an integer results in a float.

We see the same thing in the next few examples for subtraction and multiplication.

Division is a bit different. 2 / 2 results in a float, 2.o. In order to integer division, div must be used:

> div(2,2)
1
> div 2,2
1

We can also get the remainder by using rem:

> rem 26,5
1

If the rem example seems odd, here’s a visualization:

Finally, let’s use round a float to an integer like so:

> round(2.2)
2

Booleans

Just like JavaScript, Booleans are true or false:

> true
true
> false
false

The only thing that isn’t true (besides false) is nil:

> nil
nil

Atoms & Modules

Atoms are constants whose names are also their values:

> :test
:test
> :test == :compare
false

In JavaScript, the equivalent to these are symbols which were introduced in ES6:

var sym = Symbol('foo');

Their only purpose in JavaScript is to be an identifier of object properties.

In Elixir, names of modules are also atoms.

Modules are namespaces for functions. We will look at these later in this chapter. However, if we had a module called Test and a function within that module called func, we would call it like so (don’t run this):

> Test.func
*result from function*

Even Test has not been declared yet, it will also be an atom:

> is_atom(Test)
true

Atoms can also be used to reference external and built-in Erlang libraries:

Strings

Strings are pretty easy to grasp. However, strings must be in double quotes:

> "string"
"string"

We can print strings using IO.puts (stands for input/output):

> IO.puts "hello"
hello
:ok

As you can see, printing a string also the atom :ok.

We can also use IO.gets to capture a string from an input:

> myFav = IO.gets "Elixir or JavaScript?\n"
Elixir or JavaScript? //prompt
Elixir //what we type in
"Elixir\n" //captured string
> myFav
"Elixir\n"

In the code above, we display a prompt, type in our response, see the captured string, and then check the value of myFav which is Elixir\n.

\n puts in a new line when we print. You can think of this like <br/> in HTML.

We can see this in action by printing myFav:

> IO.puts myFav
Elixir
:ok

The equivalent of the + (JavaScript) for string concatenation in Elixir is <>:

> IO.puts "Hello" <> " World"
Hello World
:ok

Comparison Operators

Let’s take a moment to look at comparison operators. There is nothing too unusual for a JavaScript developer:

> 2 == 2.0
true
> 2 === 2.0
false
> 2 > 3
false
> 2 < 4
true
> 2 <= 2
true
> 4 >= 3
true
> 2 != 2
false
> 2 !== 2.0
true

=== and !== are strict comparisons that check the type and value. As we saw in the examples above,

2 === 2.0 is false because Elixir has integers and floats unlike JavaScript which simply has number.

We can also do string comparison of any types:

> 2 === "string"
false

Lists

Lists are collections of values that can contain multiple types within brackets:

> list = [1, "a", :test]
[1, "a", :test]
> list
[1, "a", :test]

In Elixir, lists are implemented as linked lists. Linked lists are linear data structures where each element is a separate node. [1]

Visually, they would look like this:

In order to read all the values within a linked list, we traverse from the head to the end (where next is null). The head is the first value and the rest of the list is the tail.

We can do some cool things with lists such as concatenation using ++:

> list = [1,2] ++ [3,4]
[1, 2, 3, 4]

Note: In most cases, you should concatenate a value to the front of a list (prepend) like so:

> list = [1,2,3]
[1, 2, 3]
> ["soup"] ++ list
["soup", 1, 2, 3]

We can also subtract from lists like so:

> list -- [1,2]
[3, 4]

Another cool feature is that we can easily extract the head and tail of a list using hd and tl:

> hd [3,2,1]
3
> tl [3,2,1]
[2,1]

We can also store the head and tails values from lists really easily using pattern matching:

> [storeA | storeB] = [1, "chicken", "soup"]
[1, "chicken", "soup"]
> storeA
1
> storeB
["chicken", "soup"]

Tuples

According to Elixir School, “Tuples are similar to lists but are stored contiguously in memory.” I’m sure that might not make sense to everyone right away, so here’s the full explanation.

Tuples also look much like lists, however, they are within curly braces:

{3.14, :pie, "Apple"}

One important use of tuples is that they can be returned from functions.

We can extract a value in a tuple using elem(*tuple*, *index*):

> tuple = {1,2}
{1, 2}
> elem(tuple, 0)
1

Notice that the index starts at 0.

Keyword Lists

Keyword lists are lists that associate a key with a value:

> [chicken: "noodle", soup: "is lit"]
[chicken: "noodle", soup: "is lit"]

In the example above, chicken: and soup: are both atoms. chicken: “noodle” and soup: “is lit” are both tuples.

The example could also be written like so:

> [{:chicken, "noodle"}, {:soup, "is lit"}]
[chicken: "noodle", soup: "is lit"]

Maps

Maps are another associative collection (store with key and value association). Maps are defined with %{}:

> example = %{:hey => "you", "squadron" => :up}
%{:hey => "you", "squadron" => :up}
> example[:hey]
"you"
> example["squadron"]
:up

We can also fetch a value which is store in a tuple with a preceding :ok atom:

> Map.fetch(example, :hey)
{:ok, "you"}

Enumerating

Elixir comes with a built-in module called Enum which allows us to enumerate through lists, keyword lists, and maps to do some powerful stuff.

Let’s test this out.

Enum.all?

We can use Enum.all? to enumerate through a collection and return true if every item in the collection meets a condition.

For example:

> list = ["foo", "bar", "hello"]
["foo", "bar", "hello"]
> Enum.all?(list, fn(item) -> String.length(item) == 3 end)
false

A couple things to note.

First, the code above can be read as: “Enumerate through each item in our list and return true if every item is a string with 3 characters.

Second, note the syntax for this:

Enum.all?(*insert list, *anonymous function*)

The anonymous function in this example, fn(item) -> String.length(item) == 3 end is just like the following in JavaScript:

list.map((item) => {....})

There’s a couple differences:

  • fn comes before the parameter.
  • The arrow is “skinny” not “fat”.
  • We don’t need a return statement.
  • We end an anonymous function using end.

Enum.any?

This is similar to the previous example except it will return true if any item meets the condition:

> Enum.any?(list, fn(item) -> String.length(item) == 3 end)
true

Enum.chunk

Enum.chunk breaks a collection into smaller groups using the following syntax:

Enum.chunk(*insert collection*, *break into groups of this size*)

For example:

> Enum.chunk([1, 2, 3, 4], 2)
[[1,2], [3,4]]

Note: If there’s not enough items left to form a group with the specified size, the remaining items are omitted:

> Enum.chunk(list, 2)
[["foo", "bar"]]
> list
["foo", "bar", "hello"]

We can also chunk based on a condition other than size using Enum.chunk_by:

> Enum.chunk_by(list, fn(item) -> String.length(item) == 3 end)
[["foo", "bar"], ["hello"]]

As you can see above, all three items met the condition and there’s one group of two items and another group with just one item. It always follows this pattern:

> Enum.chunk_by(["one", "two", "three", "four", "five"], fn(x) -> String.length(x) end)
> [["one", "two"], ["three"], ["four", "five"]]

Enum.map_every

What if we wanted to go through a collection and do something with every nth item? That’s where Enum.map_every comes in:

> numbers = [1,2,3]
[1, 2, 3]
> Enum.map_every(numbers, 2, fn(number) -> number + 1 end)
[2, 2, 4]

As you can see, it applies the function on the first item and every nth item after that.

Enum.each

This is used if we want to iterate through a collection but not create a new collection.

For example, we can print out a list:

> Enum.each(list, fn(item) -> IO.puts(item) end)
Foo
Bar
Hello
:ok

Enum.map

If we do want to iterate through a collection and create a new collection with updated values, we can use Enum.map:

> numbers = [1, 2, 3, 4]
[1, 2, 3, 4]
> Enum.map(numbers, fn(number) -> number * 2 end)
[2, 4, 6, 8]

Enum.min & Enum.max

As you can guess, these return the min and max of a collection respectively:

> Enum.min(numbers)
1
> Enum.max(numbers)
4

Enum.reduce

Enum.reduce takes in a collection and an accumulator and “reduces” the collection to a single number. Make sense? No way, Mike!

I figured. Let’s explain by looking at an example:

> Enum.reduce([1, 2, 3], 5, fn(item, acc) -> item + acc end)
> 11

What the shoot?! How did give us 11?!

Let’s walk through it by each iteration:

1) item is 1 and acc is 5. 6 (1 + 5) then becomes the new value of the accumulator.accumulator: 62) item is now 2 and acc is 6. 8 (2 + 6) then becomes the new value of the accumulator.accumulator: 83) item is now 3 and acc is 8. 11 (3 + 8) then becomes the new value of the accumulator.Final accumulator and final value returned: 11

In the example above, a single value is accumulated and returned hence accumulator.

If no accumulator is specified, it is initialized as 0

> Enum.reduce([1, 2, 3], fn(item, acc) -> item + acc end)
6

Enum.sort

We can use this to sort a collection:

> numbers = [4,99,1]
[4, 99, 1]
> sorted = Enum.sort(numbers)
[1, 4, 99]
> sorted
[1, 4, 99]

Enum.uniq

The final one to discuss is Enum.uniq which simply removes duplicate values:

> dups = [4,1,2,1]
[4, 1, 2, 1]
> Enum.uniq(dups)
[4, 1, 2]

As you can see, it removes duplicates from the end.

That completes the enumerations which showcase how much power we can pack in small pieces of code with functional programming in Elixir.

Don’t worry. The fun won’t end there.

Pattern Matching

So far, there’s been multiple examples where we bound a value to a variable like so:

numbers = [1, 2, 3, 4]

Well, the = is not what you may be used to in JavaScript and other languages. In addition to binding variables, it can be used to store values from data structures into individual variables.

Here’s an example:

> [a,b,c] = [1,2,3]
[1, 2, 3]
> a
1
> b
2
> c
3

Neat!

Now, this only works if both sides are the same size and the same type:

> [a,b] = [1,2,3]
** (MatchError) no match of right hand side value: [1, 2, 3]
> {a,b,c} = [1,2,3]
** (MatchError) no match of right hand side value: [1, 2, 3]

The second error is produced because the left-hand collection is a tuple but the right-hand collection is a list.

Control Structures

Control structures may sound complicated, but we’re just going to be looking at handling conditions.

If/Else

An if/else has the following syntax in Elixir:

if *condition is true* do
//something
else
//do something else
end

Let’s give this a shot:

> x = 1
1
> y = 2
2
> if x === y do
...> IO.puts "Cool beans!"
...> else
...> IO.puts "Oh shoot!"
...> end
Oh shoot!
:ok

We could also do if/else if/else which would look like this:

Unless

Here’s a cool control structure distinct from JavaScript.

We can replace an if with unless like so:

> game = "mass effect andromeda"
"mass effect andromeda"
> unless game = "mass effect andromeda" do
...> IO.puts "Sure! I'll play!"
...> else
...> IO.puts "No thanks!"
...> end
No thanks!
:ok

Case

Case is used to compare a value against many patterns until we find a matching one. It’s similar to a switch in JavaScript.

> case [1,2,3] do
...> [1,2,3] -> IO.puts "Match!"
...> [4,2,3] -> IO.puts "No Match!"
...> [1,2,x] -> IO.puts "Match first two items and x would equal 3"
...> _ -> IO.puts "Wildcard: match any value"
...> [_,_,3] -> IO.puts "Match any value for the first two."
...> end
Match
:ok

Because you can bind items and use wildcards, this is extremely useful yet beautifully condense.

Cond

If you want to match conditionals instead of values, use cond:

> cond do 
...> 1 === 2 -> "LOL"
...> 1 === "pie" -> "It better be cherry!"
...> 1 === 1 -> "Squadron up!"
...> end
"Squadron up!"

Functions, Modules, & Pipe Operator

The final section…woohoo!

Anonymous Functions

We already discussed anonymous functions which looked like this:

increase = fn(number) -> number + 1 end

There’s also a shorthand way of writing this:

increase = &(&1 + 1)

The first & can be thought of as a replacement of fn() ->. &1 refers to the first (and only) parameter which we want to increment by 1 and return. The end is also omitted.

Named Functions and Modules

For a named function, we write that within a module which we already mentioned is a namespace for one or more functions:

defmodule Animals do
def dog(name) do
"Bark bark " <> name
end
def cat(name) do
"Meow meow " <> name
end
end

defmodule defines a module which we called Animals. Within it, we have two functions, dog and cat, which are proceeded with def.

To call our functions, we would do:

> Animals.dog("Max")
"Bark bark Max"
> Animals.cat("Daisy")
"Meow meow Daisy"

Important Note: You must keep module names capitalized and named functions lowercase.

Pipe Operator

In our code, we will have multiple functions. There will be many cases when we want to take the result of one function and use it as a parameter for another function.

Here’s one way we could write it:

//HelloWorld.hello returns "Hello"
//myModule.world(param) returns param <> " World"
> HelloWorld.world(myModule.hello)
"Hello World"

Now, this isn’t too confusing for a simple example. However, there’s a cleaner way to write this using the pipe operator, |>:

> HelloWorld.hello |> HelloWorld.world
"Hello World"

Here’s the full example:

We can also use this to simply separate parameters from functions:

> "hello" |> HelloWorld.world
> "Hello World"

Extra Practice

That’s all she wrote! We have completed a walk through of the basics of Elixir. Hopefully, this was fun and exciting. We should have all we need to dabble into Phoenix, however, I would encourage you to play around and do some exercises.

I recommend finding Python practice problems and completing them with Elixir.

Other Resources:

Elixir School
Getting Started Guide
Official Docs
Programming Elixir

Happy Hacking! 💪

Up Next

In the next chapter, we will go an introduction into the concepts and terminologies behind Phoenix and complete our first Phoenix app together.

Chapter 3

Chapter 3 is now available.

Sign Up for Notifications

Get notified when each chapter is released.

Cheers,
Mike Mangialardi
Founder of Coding Artist

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store