Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Phoenix Web Development

You're reading from   Phoenix Web Development Create rich web applications using functional programming techniques with Phoenix and Elixir

Arrow left icon
Product type Paperback
Published in Apr 2018
Publisher Packt
ISBN-13 9781787284197
Length 406 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Brandon Richey Brandon Richey
Author Profile Icon Brandon Richey
Brandon Richey
Arrow right icon
View More author details
Toc

Table of Contents (14) Chapters Close

Preface 1. A Brief Introduction to Elixir and Phoenix FREE CHAPTER 2. Building Controllers, Views, and Templates 3. Storing and Retrieving Vote Data with Ecto Pages 4. Introducing User Accounts and Sessions 5. Validations, Errors, and Tying Loose Ends 6. Live Voting with Phoenix 7. Improving Our Application and Adding Features 8. Adding Chat to Your Phoenix Application 9. Using Presence and ETS in Phoenix 10. Working with Elixir's Concurrency Model 11. Implementing OAuth in Our Application 12. Building an API and Deploying 13. Other Books You May Enjoy

Introducing IEx and Elixir

We'll start off by talking a little bit about the Elixir programming language that Phoenix sits atop. The most important thing to note, especially if you're coming from another programming language such as Ruby, Go, PHP, JavaScript, or Java, is that there is one significant difference between Elixir and the rest: Elixir is a functional language. While this doesn't change everything dramatically, it does introduce a few gotchas that we will need to get out of the way.

If you're already comfortable with Elixir and IEx, feel free to skip this section, as this is intended to be a quick introduction or refresher to the Elixir language and the IEx debugging tool.

Before we dive too far into those gotchas, let's first get acquainted with a tool that is going to make our development process in Elixir much simpler: IEx.

What is IEx?

The good news: Elixir includes a tool called a Read-Evaluate-Print-Loop (REPL), which is essentially an Elixir shell. You can think of this as being similar to an irb in Ruby or the node shell in Node.js. It is a very detailed and helpful tool that we'll be referring to quite a bit in our journey of building this full web app! To open REPL up (assuming you have Elixir installed), run the following command:

$> iex

You should see something similar to the following snippet appear on your screen:

$> iex
Erlang/OTP 20 [erts-9.0] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
Interactive Elixir (1.5.0) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>

Now we can start messing around with some sample Elixir code! Let's start off with a couple of base scenarios.

Variables in Elixir

Like most other programming languages, Elixir has a concept of variables. Variables are, simply put, just a place to store values or data. Variables in Elixir do not require any sort of type definition or declaration that they're variables, as you might see in languages such as JavaScript. We can declare a new variable as simply as follows:

iex(1)> greeting = "Hello There"
"Hello There"
In IEx, the output of any statement entered is always the next line in the shell.

Variables point to memory locations, so when you use them in your code, the values stored in those memory locations are automatically pulled out for you, as shown in the following example:

iex(2)> greeting
"Hello There"

While variables can be reassigned in Elixir, the actual values are immutable! This brings us to our first gotcha in Elixir: immutability.

Immutability in Elixir

The first thing to note is Elixir's concept of immutability. This means that any variables used will retain their value whenever they're passed along. This is a big difference from languages such as Ruby, where the state of any data you pass on is no longer guaranteed to retain its original value. Objects are a good example of this in Ruby.

To demonstrate this concept a little bit better (don't worry about the syntax yet; we'll get to that!), let's take a look at a code snippet in Javascript, as follows:

function upcase_name(p) {
p.name = p.name.toUpperCase();
}

var person = { name: "Brandon" } // Name here is still 'Brandon'
person // Output is { name: "Brandon" }
upcase_name(person)
person // Oh no! Output is now { name: "BRANDON" }

Oh no! We've just introduced a scenario where calling a function is destructive whether we've intended it to be or not! Let's look at a similar block of code in Elixir in the following example:

upcase = fn p ->
Map.put(p, :name, String.upcase(p.name))
end

person = %{ name: "Brandon" } # Name here is still 'Brandon'
upcase.(person)
person # Output for name is still 'Brandon'!

This is likely our intended behavior, and it prevents people from doing wacky things while programming, such as writing functions that end in? (typically used to denote a function that answers a question about the arguments in a Boolean). (Sadly, I've seen this more times than I'd care to admit).

Of course, none of these examples are without their own respective gotchas (and honestly, which programming language nowadays doesn't have its fair share?). For example, the following snippet is considered completely legitimate code in Elixir:

person = %{ name: "Brandon" }
person = %{ name: "Richey" }

But, wait! I hear you cry, I thought Elixir's variables are immutable!Well, the answer to that is that they are immutable! Or rather, the memory locations that they point to are; therefore, every time we create a variable we're essentially creating a pointer to a location in memory. In our case, it stores our previous map into some memory location (we'll call it 0 x 01). When we reassign a person, however, we store the value of %{ name: "Richey" } into a different memory location (we'll call it 0 x 02). This is what is being passed along to our functions when we pass in the person variable; here, we're basically telling Elixir, Hey, use the value stored in memory location 0 x 01, which is what the variable person is currently pointing at. This means that we don't have to worry about the value in 0 x 01 being changed as long as it is still in use.

Understanding the different types in Elixir

Elixir has a number of different built-in types that we'll be working within the course of building our applications. They are the following:

  • Strings (sometimes referred to as binaries)
  • Integers
  • Floats
  • Lists
  • Maps
  • Keyword lists
  • Tuples
  • Modules
  • Functions (functions themselves break into two separate types: module functions and anonymous functions)

Let's take a look at the various representations of these types and use some of the tools in IEx to understand more about what we're working with.

Getting more information with the i helper

IEx has a really handy helper function available to us: i. This function comes in two separate arities, i/0 and i/1 (arities, in this case, being the number of arguments that we have to supply to each version of the function). i/0 displays information about the last value output in IEx, whereas i/1 displays information about a particular value or variable. Let's take a look at a few examples:

iex(3)> x = 5
5
iex(4)> i
Term
5
Data type
Integer
Reference modules
Integer
Implemented protocols
IEx.Info, Inspect, List.Chars, String.Chars

Let's talk about what the preceding example is giving us. First, it tells us the Term, which almost literally means the value itself. Next, we have the Data type, which tells us what Elixir is classifying this particular value as (in the example, it's an integer). Next, it tells us the Reference modules, which is a fancy way of telling us what modules we should use to be able to interact with this particular data type. Finally, it gives us a list of implemented protocols. These are additional modules that provide some sort of behavior that we expect that data type to adhere to if it implements a particular behavior. You can think of them as being similar in concept to interfaces in other programming languages.

Getting more information with the h helper

IEx also provides us with another incredibly helpful built-in function: h. Similar to i, h has two arities available for us to use: h/0, which displays the help page for IEx, and h/1, which displays the help for a particular module. So, while the following example won't work:

iex(5)> h 5
Invalid arguments for h helper: 5

This following example will:

iex(6)> h Integer
Integer

Functions for working with integers.

Using i and h effectively will go a long way towards helping us understand more of Elixir as we go along!

Using IEx and helpers to understand types

Going back to our integer example, we saw that there were a few different protocols that integers implement that we can take advantage of, but they may not make immediate sense to us. Let's use String.Chars as an example, where we'll call the h helper on String.Chars to learn more about the module from its documentation:

iex(7)> h String.Chars
String.Chars
The String.Chars protocol is responsible for converting a structure to a binary
(only if applicable).
The only function required to be implemented is to_string/1, which does the
conversion.
The to_string/1 function automatically imported by Kernel invokes this
protocol. String interpolation also invokes to_string/1 in its arguments. For
example, "foo#{bar}" is the same as "foo" <> to_string(bar).

Neat! It's like having a language guide built into our programming environment. It's difficult to stress just how useful this ends up being in practice, especially if you're like me and like doing programming where there's little access to the internet and you need to figure out how to use something like String.Chars. Reading the previous example, we see one particular snippet:

The only function required to be implemented is to_string/1, which does the conversion.

Let's dive into that further, again using h/1, as shown in the following example:

iex(8)> h String.Chars.to_string/1
def to_string(term)
Converts term to a string.

The preceding snippet tells us a huge amount about that particular module and function. Based on the description and provided function signature, we can infer that the Integer module implements a String.Chars protocol, which that means it needs to implement a to_string/1 function that matches the preceding function signature. Therefore, we can further infer that the way to convert an integer to a string is with the following method:

iex(9)> Integer.to_string(5)
"5"

Et voila! We've followed the chain of using i/1 and h/1 to figure out exactly how to perform an operation on a particular data set, as well as what assumptions we can make about the protocols implemented for that particular data type, and so on. Given this particular revelation, let's start expanding on this a little bit more and take a look at some of the other data types that exist in Elixir and a sample of the other operations that we can perform on them:

iex(10)> i 1.0
Term
1.0
Data type
Float
Reference modules
Float
Implemented protocols
IEx.Info, Inspect, List.Chars, String.Chars

So, if we wanted to represent something with decimal places, we'd use a float. Types in Elixir are inferred, so you don't have to explicitly specify the type for each variable. In addition, you can store any type in the same variable when you reassign it, so something like the following snippet is a perfectly valid operation (despite being very bad practice):

iex(11)> x = 5
5
iex(12)> x = "Hello"
"Hello"

If you're coming from a language such as Ruby or JavaScript none of this will be very surprising, but if you're coming from a language that is strongly-typed, this might be a little more off-putting. There are stricter ways to enforce types using tools such as Dialyzer, but in my experience, I've found those to be used relatively rarely. Let's now try using the information helper on a string of data to see what information we get back from IEx. Take a look at the following example:

iex(13)> i "Hello There"
Term
"Hello There"
Data type
BitString
Byte size
11
Description
This is a string: a UTF-8 encoded binary. It's printed surrounded by
"double quotes" because all UTF-8 encoded codepoints in it are printable.
Raw representation
<<72, 101, 108, 108, 111, 32, 84, 104, 101, 114, 101>>
Reference modules
String, :binary
Implemented protocols
IEx.Info, Collectable, Inspect, List.Chars, String.Chars

Here, we see that we have a standard string and that there are a lot of the same implemented protocols that we saw on floats and integers as well. We see the same few protocols (as well as a new one, Collectable). Now, if we want to see the operations provided by one of the reference modules (string in our case), IEx provides another awesome way to get that information out. In our IEx console, we can simply type in String. (notice the period!) and then hit Tab on our keyboard, for example:

iex(14)> String.
Break Casing Chars
Normalizer Tokenizer Unicode
at/2 capitalize/1 chunk/2
codepoints/1 contains?/2 downcase/1
duplicate/2 ends_with?/2 equivalent?/2
first/1 graphemes/1 jaro_distance/2
last/1 length/1 match?/2
myers_difference/2 next_codepoint/1 next_grapheme/1
next_grapheme_size/1 normalize/2 pad_leading/2
pad_leading/3 pad_trailing/2 pad_trailing/3
printable?/1            printable?/2            replace/3
replace/4 replace_leading/3 replace_prefix/3
replace_suffix/3 replace_trailing/3 reverse/1
slice/2 slice/3 split/1
split/2 split/3 split_at/2
splitter/2 splitter/3 starts_with?/2
to_atom/1 to_charlist/1 to_existing_atom/1
to_float/1 to_integer/1 to_integer/2
trim/1 trim/2 trim_leading/1
trim_leading/2 trim_trailing/1 trim_trailing/2
upcase/1 valid?/1

Let's use h/1 again to get a little more information about a particular string and the operations we can perform on it, as shown in the following snippet:

iex(15)> h String.replace/4
def replace(subject, pattern, replacement, options \\ [])
Returns a new string created by replacing occurrences of pattern in subject with replacement.
The pattern may be a string or a regular expression.
By default it replaces all occurrences but this behaviour can be controlled through the :global option; see the "Options" section below.

## Options
• :global - (boolean) if true, all occurrences of pattern are replaced with replacement, otherwise only the first occurrence is replaced. Defaults to true
• :insert_replaced - (integer or list of integers) specifies the position where to insert the replaced part inside the replacement. If any position given in the :insert_replaced option is larger than the replacement string, or is negative, an ArgumentError is raised.

# ...

That's a lot of information, yes, but it's also all incredibly useful. Let's start with a very simple operation and replace the e in our greeting variable to an x. We see that the signature for String.replace/4 is replace(subject, pattern, replacement, options \\ []). Given that, let's quickly create a greeting variable and change every e to an x:

iex(1)> greeting = "Hello"
iex(2)> String.replace(greeting, "e" ,"x", global: true)

"Hxllo"

Your objects have no power here

Another thing that might be a little more difficult to contend with, especially if you're coming from a language where object-oriented programming is treated as a first-class citizen, is that objects are not a thing you can use in Elixir to organize your code or group data and functionality together. Again, let's use JavaScript as an example, as follows:

var greeting = "Hello There"
greeting.toUpperCase() # Convert a string to upper case by calling a function on the "greeting" string object
# End result: "HELLO THERE"

Here, you'll instead want to get used to the concept of using modules and functions to accomplish the same things. You can think of modules as a collection of related functions that operate on particular sets or types of data, or are otherwise grouped together by some sort of common element. So, the preceding block of code would instead be written as the following in Elixir:

greeting = "Hello There"
String.upcase(greeting) # Results in the same "HELLO THERE" message as the Javascript example

This is going to be a very common pattern that you should familiarize yourself, as you will frequently see function called as [Module Name].[Function Name]([Arguments]). Try to shy away from calling functions on objects and data and instead get used to calling functions with the module name.

You're not required to do this; there are actually ways to shorten this even further through the use of the alias and import statements!
You have been reading a chapter from
Phoenix Web Development
Published in: Apr 2018
Publisher: Packt
ISBN-13: 9781787284197
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image