# Welcome!

Functional programming can be an intimidating subject to approach. Your first encounter with it might be during CS2030S or hearing about Haskell. While using functional programming involves a shift in the way you think of problems, it does not have to be as intimidating as it seems.

In this guide, I will be diving into the application of functional programming in web development using my favorite programming language: [Elixir](https://elixir-lang.org/)!

## Why Elixir?

Besides being my favorite programming language, I think Elixir provides an incredibly gentle introduction to core [functional programming language principles](/why-functional-programming#what-makes-functional-programming-amazing). It does not have an overly strict type system or mandate pure functions for everything. While these may go against the very purist idea of functional programming, it lowers the barrier to entry to functional programming and applying it to real-world projects.

## Using this guide

This guide aims to...

:white\_check\_mark: Allow you to understand fundamental Elixir syntax to follow along

:white\_check\_mark: Give you a new perspective on what functional programming is

:white\_check\_mark: Develop a basic intuition for solving problems in a "functional programming" way

This guide does not...

:x: Try to convince you that functional programming is by any means easier or superior to other paradigms

:x: Provide a deep explanation of various constructs in Elixir like macros and IO

:x: Teach you everything about Phoenix, for that, please use the official documentation instead

:x: Claim that Elixir is the only production ready functional programming language, it is just a personal preference!

## Who am I?

I'm [Jia Hao,](https://woojiahao.com/) a computer science undergraduate from the National University of Singapore and a coreteam member of NUS Hackers. I started using Elixir in 2020 for Advent of Code and fell in love with it. Since then, I have been using Elixir for [personal projects](https://github.com/woojiahao/life) and have written several articles about Elixir (some of which have been featured on newsletters!). I also did my summer 2023 internship using Elixir at [Betafi](https://www.betafi.co/) where I was using Elixir for full-stack development of the web portal and was exposed to working with Elixir on a production level!&#x20;

## Contact me

If you have enjoyed reading this guide, wish to ask any clarifying questions, spotted an issue with the guide, or just wanna chat, do drop me an email at <woojiahao1234@gmail.com>.


# Prerequisites

## Technical prerequisites

To follow along with this guide, please ensure that you have the following setup on your machine.

1. Install Elixir locally, for more instructions, refer to [this guide](https://elixir-lang.org/install.html)
2. Install SQLite locally (it should be preinstalled for MacOS and Linux users), for Windows users, refer to [this guide](https://www.tutorialspoint.com/sqlite/sqlite_installation.htm)&#x20;
3. Clone the demo repository for the code snippets

```
git clone https://github.com/woojiahao/practical_elixir_demo
```

```
cd practical_elixir_demo/
```

```
mix setup
```

### Using the demo repository

The demo repository contains four key branches:

1. `main`: contains the code for base Phoenix
2. `liveview-base`: contains the code for migrating from base Phoenix to Phoenix LiveView without any further additions
3. `liveview`: contains the completed code for Phoenix LiveView (including creating to-dos and marking to-dos as done/not done)
4. `complete`: contains the fully completed code with Phoenix LiveView and data persistence with SQLite3

To view the code on each branch, use the following commands:

```
git fetch
```

```
git switch main/liveview-base/liveview/complete
```

## Slides

You can find the slides for this workshop [here](https://github.com/woojiahao/talks/blob/main/20240215-practical-functional-programming/slides.pdf).

## Other prerequisites

Aside from these technical requirements, this guide assumes that you have some programming fundamentals (i.e. loops, basic understanding of recursion, variables, statements, etc.). More on these concepts will be introduced but it will be useful to have some fundamentals.


# Why functional programming?

According to [Wikipedia](https://en.wikipedia.org/wiki/Functional_programming):&#x20;

> ...functional programming is a programming paradigm where programs are constructed by applying and composing functions.
>
> ...functions are treated as first-class citizens...

## Defining functional programming

To some of you, that might seem like a bunch of technical jargon. To explain each component of the definition:

1. Functions: best to think of it as a machine that receives inputs and (sometimes) produces outputs
2. Programming paradigm: a way of writing code that also influences how you think/approach problems programmatically
3. Applying and composing functions: functional programming uses functions as basic building blocks and you can build programs from these fundamental units of work
4. First-class citizens: the core idea of functional programming is to treat functions as the fundamental building blocks so you can pass functions around to other functions (more on this later on)

Essentially, you can think of functional programming as a an alternative way of thinking when approaching problems that uses functions as the fundamental building block of systems (rather than objects in object-oriented programming)!

## Tackling misconceptions

Functional programming can seem intimidating at first because of the misconception that to work with functional programming, you will have to understand concepts like [monads](https://en.wikipedia.org/wiki/Monad_\(functional_programming\)) and [functors](https://en.wikipedia.org/wiki/Functor_\(functional_programming\)).&#x20;

While you will eventually be exposed to such concepts, you do not need to worry about them starting out. If you have ever used the `map` function in your favorite programming language, you have applied (some form of) functional programming!

Elixir (in my opinion) provides an incredibly gentle introduction to functional programming while providing the necessary tools and knowledge to appreciate and understand other (traditionally more intimidating) functional programming languages.

## What makes functional programming amazing?

While there are many concepts that branch from functional programming, functional programming finds its roots in some of these key principles:

### First-class functions

This concept allow you to pass functions around as though they are normal arguments.&#x20;

This allows you to compose very complex functions and enable concepts like [partial application](https://en.wikipedia.org/wiki/Partial_application) and [currying](https://en.wikipedia.org/wiki/Currying) (both of which are not concepts you will interact with if you're working on web development).

### Pure functions

Functional programming emphasizes the use of pure functions, which are functions that are free of "side effects" (i.e. no unintended changes happen to variables/state/files outside of the scope of the function).&#x20;

This allows you to think of such functions as black boxes that receive some input while producing some output in a deterministic manner. For instance, a function $$f(x) = x \* 2 + 1$$ is always going to produce the value $$5$$ when an input of $$x = 2$$ is given. By creating pure functions, you are ensured that when $$f(x)$$ is executed, the state of the program will always remain the same (apart from the output of the function).

The key reason why pure functions are amazing is the guarantee that the state of the program changes in a deterministic and predictable manner. This helps to reduce the "what-ifs" when calling functions.

### Recursion

Rather than relying on iterative `for` or `while` loops, functional programming encourages the use of recursion to perform iteration. This is further enhanced by the use of [tail recursion](https://en.wikipedia.org/wiki/Tail_recursion) where recursive calls at the end of functions do not take up additional space on the stack.

Although that might seem like a mouthful of mumbo jumbo, the key takeaway about this is that recursion in most functional programming languages have an equivalent performance for using recursion to its iterative counterparts, hence having no performance penalty!

Another bonus of using recursion is that it trains your ability to look at problems from a different perspective as it breaks your preconceived notions of iteration (which is an amazing thing!)

### Referential transparency

Often referred to as "immutability", functional programming emphasizes the use of immutability. This means that the value of variables cannot be changed after initialization. When a variable is initialized again, it is effectively a different copy of the variable being initialized.

For instance:

```
def fn(x) do
    x = 10
end

x = 6
fn(x)
```

The above code snippet does not re-assign the original `x` to be `10` when `fn` is called. Instead, `x` is treated as an entirely new variable when it is re-assigned.

Referential transparency is very useful when dealing with concurrent code (this is why Elixir was built to follow the functional programming paradigm actually!) and when you want to guarantee function purity.

### Type system

A big component of functional programming languages is their type system which essentially defines a set of rules that govern how/what a specific "type" can contain. A very practical example of why types are useful is the use of type aliases.

For instance, let's say you are building a shape module and you are dealing with multiple shapes that have a `float width` field. Rather than always declaring this `width` field as a `float` (which can be error prone in the event where you choose to change `width` to be an `int` or `double`), you can declare a type alias to the float type, such as `@type width :: float` so next time, you can use `width w` and if you change the type `width` to use an `int` instead, you only need to do it once where the type alias was declared!

There are many other benefits to a powerful type system but those will not be covered in this guide.


# History of Elixir

Elixir was first developed in 2011 by Jose Valim, a former Ruby on Rails core contributor. Jose started working on Elixir after noticing that there was an increase in demand and interest in distributed and parallel computing, something that Ruby and Ruby on Rails was not able to fully handle.

Jose took inspiration from two main sources. The first was the functional programming paradigm, more specifically the [referential transparency](/why-functional-programming#referential-transparency) principle. By ensuring immutability of state, Elixir was able to avoid an entire class of problems that plagued imperative languages like Ruby, caused by race conditions.

The second point of inspiration for Elixir was the Erlang virtual machine (and by extension the Open Telemetry Platform or OTP). Erlang was first designed to be used for telecom systems that emphasized a need for not only highly concurrent systems, but also systems that were distributed across devices/servers. This resonated with Jose as he wanted to build a language that not only supported concurrency within the machine, but to also be able to communicate seamlessly with other machines.

This eventually led to the birth of Elixir and the community has been growing since then. Elixir has seen itself used in many domains, such as [IoT,](https://nerves-project.org/) [web development,](https://www.phoenixframework.org/) and [distributed computing.](https://discord.com/blog/how-discord-scaled-elixir-to-5-000-000-concurrent-users)


# Elixir fundamentals

This guide aims to provide you with the fundamentals to start using Elixir with Phoenix for web development. It is by no means an exhaustive guide on the fundamentals of Elixir. For more in-depth information about Elixir, refer to the [official documentation.](https://hexdocs.pm/elixir/introduction.html)

{% hint style="info" %}
This guide assumes that you have fundamental programming knowledge of concepts like integers/floats/booleans, conditionals, and loops.
{% endhint %}

## Getting started

To try out Elixir, you can use the interactive shell (IEx). This should be automatically installed when you have [installed Elixir](/prerequisites). Go to your terminal and type

```
iex
```

You should see a prompt like this:

```
λ ~/ iex
Erlang/OTP 26 [erts-14.2.1] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit] [dtrace]

Interactive Elixir (1.16.0) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> 
```

Then, you can try running each commands in this environment.&#x20;

Alternatively, you can also create a `.exs` file and run it via `elixir <filename>.exs`.

For this guide, you can assume that all code snippets are run in IEx unless otherwise specified.


# Types

As mentioned in [Why functional programming?](/why-functional-programming), one of functional programming's draws is the type system. This is no exception in Elixir.&#x20;

Elixir is a dynamically typed language. This means that rather than declaring the type of variables during initialization like in languages like Java (`int x = 0;`), you simply initialize the variable with the appropriate value `x = 0` and the compiler is able to recognize the appropriate type for the variable.

Elixir relies on tools like the [dialyzer](https://github.com/jeremyjh/dialyxir) to provide type checking using type annotations.

Unlike other dynamic languages like Python, Elixir's use of [referential transparency](/why-functional-programming#referential-transparency) means that the value of a variable cannot be changed once it has been initialized so all references to that variable before any re-assignments will not be affected by a re-assignment.

## Reading function signatures

There will be points in the guide where I refer to a function via the following notation: `function_name/arity`. For instance, `or/2`.

The arity of a function refers to the number of arguments that the function receives. For more information about the function, you can use `h function_name/arity` in IEx to get the help documentation.


# Basic types

Elixir supports the basic types like:

1. Integer
2. Float
3. Boolean
4. Atom
5. String

```elixir
x = 1        # integer
x = 0.5      # float
x = true     # boolean
x = :atom    # atom
x = "elixir" # string
```

## Arithmetic

To perform arithmetic on numbers, you can use the following operators:

1. `+`: addition
2. `*`: multiplication
3. `/`: floating point division
4. `-`: subtraction
5. `div(dividend, divisor)`: integer division, truncates the floating point (if any)
6. `rem(dividend, divisor)`: remainder of division (similar to `%` in other languages)

```elixir
iex(1)> 1 + 1
2
iex(2)> 2 * 3
6
iex(3)> 3 / 2
1.5
iex(4)> 3 - 2
1
iex(5)> div(3, 2)
1
iex(6)> rem(3, 2)
1
```

There are also some notable utility functions like:

1. `round(number)`: rounds a number to the closest integer
2. `trunc(number)`: retrieves only the integer part of a float
3. `is_integer(value)`: returns if given `value` is an integer (floats do not count as integers)
4. `is_float(value)`
5. `is_number(value)`

```elixir
iex(7)> round(5.7)
6
iex(8)> round(5.3)
5
iex(9)> trunc(5.5)
5
iex(10)> is_integer(5)
true
iex(11)> is_integer(5.5)
false
```

There are also other modules from Erlang like [`:math`](https://www.google.com/search?client=firefox-b-d\&q=%3Amath+erlang) that provide additional utility functions such as `pow(x, y)` and `sqrt(x)`.

## Booleans

Elixir supports `true` and `false` as boolean types (much like many other languages):

```elixir
iex(13)> true
true
iex(14)> false
false
iex(15)> false == true
false
iex(16)> false == false
true
```

Boolean operators are also supported such as `or/2`, `and/2`, and `not/1`:

```elixir
iex(17)> true and true
true
iex(18)> true and false
false
iex(19)> false or true
true
iex(20)> not true
false
```

These operators are [short circuit operators](https://en.wikipedia.org/wiki/Short-circuit_evaluation). This means that the right hand side is executed iff the left hand side is insufficient to determine the result:

```elixir
iex(21)> false and raise("This error will never be raised")
false
iex(22)> true or raise("This error willl never be raised")
true
```

## nil values

`nil` in Elixir represents the absence of a value (similar to `null` in other languages). `nil` and `false` are both considered "falsy" values (i.e. evaluates to `false`) and all other values are considered "truthy".

To operate with booleans and `nil` values, additional operators are supported: `||/2`, `&&/2`, `!/1` that correspond to and, or, and not.

```elixir
iex(23)> !nil
true
```

## Atoms

An atom is a constant whose value is its own name. They are globally unique so `:apple == :apple`. The most common use case for atoms is to signal the return value of a function such as `:ok` or `:error`.

```elixir
iex(24)> x = :apple
:apple
iex(25)> x == :apple
true
```

## Strings

Much like other languages like Java, strings are denoted using double quotes.

```elixir
iex(26)> str = "Hello world!"
"Hello world!"
```

String concatenation is achieved using `<>/2`:

```elixir
iex(27)> str <> " said the computer"
"Hello world! said the computer"
```

String interpolation (i.e. string templates/string formatting) is achieved by adding `#{}` into the string with the variable/expressions going within the curly braces:

```elixir
iex(31)> "The computer said '#{str}'"
"The computer said 'Hello world!'"
```

{% hint style="info" %}
All values are converted to a string in string interpolation
{% endhint %}

You can print to the console using `IO.puts/1` like `System.out.println()`.

To retrieve the length of a string, use the `String.length/1` function. The [`String` module](https://hexdocs.pm/elixir/1.12/String.html) contains many more string manipulation functions.

## Structural comparison

You can compare between two values using `==`, `!=`, `<=`, `>=`, `<`, and `>` operators.

```elixir
iex(32)> 1 == 1
true
iex(33)> "a" == "a"
true
iex(34)> 1 != 2
true
iex(35)> 1 < 2
true
iex(36)> 1 == 1.0
true
```

Notice that although we were comparing an integer to a float, the result is still true. This can be mitigated by using the strict comparison operators, `===` and `!==`:

```elixir
iex(37)> 1 === 1.0
false
```


# List and tuples

## Lists

Lists in Elixir are implemented as linked lists and are denoted using square brackets:

```elixir
iex(1)> x = [1, 2, 3, 4]
[1, 2, 3, 4]
```

Since Elixir is dynamically typed, you can create lists of different types that will, by default, be treated as the an `[any()]` type.

```elixir
iex(2)> x = ["hi", 1, true, :atom]
["hi", 1, true, :atom]
```

### Common list operations

List concatenation is achieved using `++/2` and list subtraction is achieved using `--/2`.

```elixir
iex(3)> [1, 2] ++ [4, 5]
[1, 2, 4, 5]
iex(4)> [1, 2, 3] -- [2, 3]
[1]
```

There are other built-in functions like `hd/1` and `tl/1` that return the head and remainder of the linked list (excluding the head) respectively. You can also use `length/1` to retrieve the length of the list.

```elixir
iex(5)> hd([1, 2, 3])
1
iex(6)> tl([1, 2, 3])
[2, 3]
iex(7)> hd([])
** (ArgumentError) errors were found at the given arguments:

  * 1st argument: not a nonempty list

    :erlang.hd([])
    iex:7: (file)
```

## Tuples

Tuples, on the other than, use curly braces instead:

```elixir
iex(7)> t = {:ok, "hello", 1}
{:ok, "hello", 1}
```

Use the `tuple_size/1` function to retrieve the length of the tuple and `elem/2` to retrieve an element of the tuple given its zero-based index.

```elixir
iex(8)> elem(t, 2)
1
iex(9)> elem(t, 1)
"hello"
iex(10)> tuple_size(t)
3
```

Tuples, unlike arrays, are of fixed size. So the size of the tuple cannot be changed (i.e. cannot add to an existing tuple).

## Lists or tuples?

Both lists and tuples are immutable data structures so any operations on an existing list/tuple will result in a new list/tuple being created.

However, lists are represented as linked lists. Thus, any operation that will require searching through a linked list will always take $$O(n)$$ time where $$n$$ is the length of the list.&#x20;

Tuples, on the other hand, are much closer to arrays in representation: being stored contiguously in memory. Thus, most tuple operations are quick, such as getting by index. However, operations that require updating the tuple can be more costly as a new contiguous memory must be located to be assigned.

{% hint style="info" %}
Elixir has some compiler-level optimizations to reduce the overall memory usage by tuples. For instance, when modifying a tuple, the existing elements are shared between the old and new tuple.
{% endhint %}

Tuples are more commonly used as return values with fixed sizes, such as `{:ok, value}` and `{:error, error}`.

## Comparing lists and tuples

The equality operators, `==` and `!=` both work on lists and tuples as well.


# Keyword lists and maps

Keyword lists and maps are known as "associative data structures" in Elixir. Associative data structures are able to associate a key to a certain value. This is often known as hash maps or dictionaries in other languages. However, there are some minor differences between Elixir's associative data structures and the traditional hash map/dictionary.

## Keyword lists

Keyword lists are a common data structure used to pass options to functions. Consider a function call with parameters to configure how the function works such as `String.split/3`:

```elixir
iex(11)> String.split("hello world  another ", " ", trim: true)
["hello", "world", "another"]
```

We are providing the additional options via a keyword list. A keyword list toward the end of a function call can omit the use of the square brackets.

Keyword lists are really just lists with 2-item tuples where the first element is the key (an [atom](#atoms)) and the second is any value that corresponds to that key.

You can create keyword lists in two ways:

```elixir
iex(12)> x = [a: 0, a: 1, b: 2]
[a: 0, a: 1, b: 2]
iex(15)> [{:a, 0}, {:a, 1}, {:b, 2}]
[a: 0, a: 1, b: 2]
```

Note that keyword lists allow duplicate keys (unlike hash maps).

### Accessing elements

When accessing a keyword list, square brackets can be used (much like dictionaries in Python):

```elixir
iex(16)> x[:b]
2
```

Note that since keyword lists support duplicate keys, using square brackets on a duplicate key retrieves the first instance:

```elixir
iex(17)> x[:a]
0
```

### Comparing keyword lists

Another key property of keyword lists is that the keys must be ordered (as defined by the developer, YOU!). This means that `[a: 1, b: 2] != [b: 2, a: 1]`. Thus, while keyword lists allow you to specify a select number of keywords for the function, it is impractical to set "expectations" on the form that these keywords lists may take.

The [`Keyword` module](https://hexdocs.pm/elixir/1.13/Keyword.html) contains more helper functions to manipulate and interact with keyword lists.

{% hint style="info" %}
**Key properties of keyword lists:**

\
1\. Keyword lists must use atoms as keys\
2\. Keyword lists are order dependent\
3\. Keyword lists support duplicate keys
{% endhint %}

Given the order dependence of keyword lists, it is not recommended to use [Pattern matching](/elixir-fundamentals/pattern-matching) with it. More on this will be covered below.

## Maps

Maps are the closer equivalent to hash maps and dictionaries in other languages. Maps essentially store key-value pairs and are defined using `%{}`:

```elixir
iex(18)> m = %{:a => 1, "hello" => "world", 2 => :c}
%{2 => :c, :a => 1, "hello" => "world"}
```

From the above example, you can see that maps are not order dependent nor do they require atoms as keys (in comparison to keyword lists).

### Accessing elements

To access elements in a map, the square bracket notation can be used (much like keyword lists):

```elixir
iex(19)> m[:a]
1
iex(20)> m[2]
:c
```

Additionally, if a key is an atom, the `.` operator can also be used (similar to how object access works in Javascript):

```elixir
iex(21)> m.a
1
```

There is also a very useful syntax for updating keys in a map via `%{map | key: new_value}`.

Similar to the `Keyword` module, the [`Map` module](https://hexdocs.pm/elixir/1.12.3/Map.html) exists to provide extra utility when working with maps.

Maps are the perfect structure to use with [Pattern matching](/elixir-fundamentals/pattern-matching) as the fields are not order dependent. More on this will be covered below.


# Pattern matching

The `=` operator is known as the match operator in Elixir. This is because the implicit behavior of `=` is to perform a match between the left hand side and right hand side of the equation. This is on top of the existing assignment behavior. As a result, the following code can be interpreted as:

```elixir
iex(1)> x = 1 # This is assigning the value 1 to variable x
1
iex(2)> 1 = x # This is matching the value of variable x to 1 (checks if LHS == RHS)
1
iex(3)> 2 = x # This fails because variable x holds value of 1, so the match fails
** (MatchError) no match of right hand side value: 1
    (stdlib 5.2) erl_eval.erl:498: :erl_eval.expr/6
    iex:3: (file)
```

As a result, `=` can be used to de-structure a variable into its "parts" as it will map all matched LHS values to the found values on the RHS. This is also known as **pattern matching**.

## Tuples

For instance, you can de-structure the contents of a tuple:

```elixir
iex(3)> x = {:a, "hello", 1}
{:a, "hello", 1}
iex(4)> {atom_var, str_var, num_var} = x
{:a, "hello", 1}
iex(5)> atom_var
:a
iex(6)> str_var
"hello"
iex(7)> num_var
1
```

Notice how each value of the tuple `x` is directly matched to a variable on the LHS. Additionally, if you attempt to de-structure/pattern match a variable with mismatched "parts", you will receive an error:

```elixir
iex(8)> {atom_var, str_var, num_var, failed_var} = x
** (MatchError) no match of right hand side value: {:a, "hello", 1}
    (stdlib 5.2) erl_eval.erl:498: :erl_eval.expr/6
    iex:8: (file)
```

## Lists

You can also perform pattern matching on lists. However, since lists are of variable length (compared to tuples), you may not know the exact number of elements in a list when pattern matching, so you can use the `|` operator instead:

```elixir
iex(8)> l = [1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5, 6]
iex(9)> [h | t] = l
[1, 2, 3, 4, 5, 6]
iex(10)> h
1
iex(11)> t
[2, 3, 4, 5, 6]
```

The `|` operator works like the `hd/1` and `tl/1` function in one.

You can optionally "ignore" a variable in pattern matching by replacing it with `_` so the part will still be matched but not assigned to any variable or compared against anything:

```elixir
iex(12)> [a | _] = l
[1, 2, 3, 4, 5, 6]
iex(13)> a
1
```

## Maps

As mentioned in [Keyword lists and maps](/elixir-fundamentals/types/keyword-lists-and-maps), while keyword lists support pattern matching, their order dependent nature makes it hard to use with pattern matching. However, maps are the perfect candidate for pattern matching against "dynamic" structures:

```elixir
iex(15)> %{:a => a, "hello" => world_var, 2 => _} = d
%{2 => :b, :a => 1, "hello" => "world"}
iex(16)> a
1
iex(17)> world_var
"world"
```

## Deeply nested structures

Pattern matching also applies to any nested structures as long as you know its "parts" ahead of time:

```elixir
iex(18)> n = [:a, %{"hello" => "world", :b => %{"nested" => "value"}}]
[:a, %{:b => %{"nested" => "value"}, "hello" => "world"}]
iex(19)> [_, %{:b => %{"nested" => nested_var}}] = n
[:a, %{:b => %{"nested" => "value"}, "hello" => "world"}]
iex(20)> nested_var
"value"
```


# Modules

## What are modules?

Modules are a way for developers to "package" like functions. For simplicity sake, you can think of modules as static classes/singletons that are initialized once when your program starts. There are other nuances behind how modules really work in Elixir but that is the fundamental behavior that you should think about at this stage.

## Your first module

To define a new module, you can use the `defmodule` [macro](https://hexdocs.pm/elixir/macros.html). This guide will not cover what macros are, but you can think of them as defining keywords with some behavior. Then, within the body of the module, you can define functions using the `def` macro. For this demonstration, we will continue using `.exs` (or Elixir scripts):

```elixir
defmodule Math do
    def add(a, b) do
        a + b
    end
    
    def subtract(a, b) do
        a - b
    end
end

IO.puts(Math.add(5, 3))
```

Then, when you run this script, you will see `8` printed.

```
λ ~/Projects/practical-elixir-demo/ main* elixir modules.exs 
8
```

## Compiling files with modules

If you are dealing with `.ex` files (as you normally would in larger projects), you can expect to compile these modules via the `elixirc` command. Then, running the `iex` command again with the resulting bytecode in the same folder will cause the `Math` module to be loaded.

```
λ ~/Projects/practical-elixir-demo/ main* elixirc math.ex
λ ~/Projects/practical-elixir-demo/ main* iex
iex(1)> Math.subtract(5, 3)
2
```

Normally, you should not have to manually compile Elixir files (mainly because it becomes unwieldy). Elixir comes with a built-in build tool called [Mix](/elixir-fundamentals/mix) that we will discuss later on.


# Functions

Functions are the core building blocks of Elixir. If you had tried the example above about modules, you would have tried creating your very first function. Unlike functions in Python and Javascript, functions cannot be declared at the top-level, outside of a module. If a function does not have a module, then it will be impossible to reference.

Functions are created using the `def` macro, where the format is as follows: `def <function_name>(<function_parameters>) do ... end`.&#x20;

## Return values

The return value of a function is automatically set as the final statement in the function body (i.e. no explicit `return` is necessary):

```elixir
def format_person(name, age, school) do
    "#{name} is #{age} years old and attends #{school}"
end
```

## Impure functions

Unlike stricter functional programming languages, Elixir does allow side effects (like printing or database editing) in a function:

```elixir
def format_person(name, age, school) do
    IO.puts("Hello world!!")
    "#{name} is #{age} years old and attends #{school}"
end
```

However, it is recommended to keep side effects to a minimum.

## Quality of life

Elixir also provides minor quality of life features when writing functions. Some of which include:

1. Omitting the parentheses when there are no parameters
2. Removing the use of `end` when there is only the return statement

```elixir
def foo, do: 5
```

## Method overloading

Similar to other languages, Elixir also supports method overloading (i.e. declaring the same method name with differing parameters). For instance:

```elixir
def foo(a, b), do: a + b
def foo(a), do: a
def foo, do: nil
```

## Default arguments

Elixir functions also supports default arguments:

```elixir
def minus(a, b \\ 0) do
    a - b
end
```

The function above receives an optional argument `b` that defaults to `0` if left unspecified. If `b` is omitted, `minus` just returns the original argument. Otherwise, it performs the subtraction operation.

## Pattern matching

Function parameters can also be pattern matched, allowing you to create really concise function definitions without the need for any other constructs. For instance, look at the implementation of the Fibonacci sequence using pattern matching:

$$
f(n) = \begin{cases}
1, n = 0\\
1, n = 1\\
f(n-1) + f(n - 2)
\end{cases}
$$

```elixir
def fib(0), do: 1
def fib(1), do: 1
def fib(n), do: fib(n - 1) + fib(n - 2)
```

Pattern matching in functions makes it so we can easily express such recurrence relations with minimal effort and in a way that makes sense semantically.

You can combine the pattern matching shown in previous examples as well! For instance, you can pattern match the parts of a map and use them within the function without any further access methods:

```elixir
def foo([_, %{:b => %{"nested" => nested_var}}]) do
    "Nested var was " <> nested_var 
end
```

If there are multiple function declarations with different pattern matching parameters, Elixir will try each of them until it finds a match. If no match is found, then an exception is raised. You can create a "base function" to handle such cases. These functions tend to be the last in the function list:

```elixir
def fib(0), do: 1
def fib(1), do: 1
def fib(n): do: fib(n - 1) + fib(n - 2)
def fib(_), do: nil # NOTE: this won't ever be called, this is for illustrations
```

{% hint style="info" %}
Try arranging your pattern matching functions in decreasing order of strictness (i.e. the most specific cases should be declared first)
{% endhint %}

## Guard clauses

Another way to validate the arguments of a function before the function body is to use guard clauses which are added after the parameter list, following the `when` keyword. This is useful if you wish to combine various pattern matching clauses, you have to check the types of the arguments, or when you need to validate the arguments against one another:

```elixir
def fib(n) when n < 0, do: nil
def fib(n) when n == 0 or n == 1, do: 1
def fib(n), do: fib(n - 1) + fib(n - 2)

def abs_minus(a, b) when a < b, do: b - a
def abs_minus(a, b), do: a - b
```

There are certain limitations to using guard clauses, one of which includes not being able to use custom functions in the guard clause (this is due to the nature of how guard clauses and functions are compiled). You can refer to this [website for more information](https://kapeli.com/cheat_sheets/Elixir_Guards.docset/Contents/Resources/Documents/index) about guard clauses.

## Anonymous functions

Anonymous functions are functions declared without using the `def` macro. They allow you to pass functions around as parameters or return values (you can do that even with regular functions too!).

You can declare anonymous functions with `fn <parameter list> -> <function body> end`:

```elixir
pow_two = fn x -> x * x end
pow_two.(4) # returns 16
```

You can also make the anonymous function multi-line by adding a newline after `->` (unlike Python's lambdas).&#x20;

Notice that you call the anonymous function using `.()` rather than just `()`, this helps make it clear that you are calling an anonymous function as you may have overriden an existing function.

## Closures

Anonymous functions have access to the variables that are in scope when the function is defined. This is also known as a closure.

```elixir
def foo do
    x = 42
    bar = fn -> x * 2 end
    bar.() # returns 84
end
```

## Currying

Closures are particularly useful when you are trying to curry functions. Currying is a functional programming technique that takes functions that accept multiple parameters and transforms them into functions that take only one parameter each.&#x20;

Closures allow each of the nested functions to reference the variables from outside of its current scope (i.e. the innermost function can reference `a` and `b`):

```elixir
def foo(a, b, c) do
    a * b + c
end

def curry_foo(a) do
    fn b ->
        fn c ->
            a * b + c
        end
    end
end

foo(1, 2, 3) # returns 5
curry_foo(1).(2).(3) # returns 5
```

Currying is useful for creating partial applications of functions. For instance, let's say you would like to apply the last operation (`* c`) with different values but preserve the values of `a = 1` and `b = 2` from the initial application, you can do so by applying the curried function twice (not three times) and then saving the partially applied function as a variable:

```elixir
partial = curry_foo(1).(2)
partial.(3) # returns 5
partial.(6) # returns 8 instead
partial.(10) # returns 12
```

So, rather than having to type `foo(1, 2, 3)` and then `foo(1, 2, 6)` and then `foo(1, 2, 10)`, you only have to call `partial.(x)` with the different variables.


# Conditionals

When writing functions you may notice that the function behaviors may differ based on the state of the arguments. While [Functions](/elixir-fundamentals/functions#pattern-matching) and [Functions](/elixir-fundamentals/functions#guard-clauses) in the function declaration may help to alleviate some of these problems, some derived variables (i.e. variables that are composed of the arguments) cannot be handled through these mechanisms and as such, will require explicit control through conditions.

## case

`case` is used to compare a given value against many patterns until a matching one is found. This is most similar to `switch` statements in other languages:

```elixir
case {1, 2, 3} do
    {1, 2, 5} -> "This will not return"
    {4, 5, 6} -> "Neither will this"
    {1, x, 3} -> "This will work with any #{x}"
    _ -> "This is used as the 'default' case"
end
```

If a pattern contains a reference to a variable outside of the `case`, you need to use the pin operator `^` which "locks" in the variable at the time of use, preventing it from being re-assigned (like in the above example with `x`):

```elixir
x = 5
case {1, 2, 3} do
    {1, 2 ^x} -> "This will also try matching {1, 2, 5} and fail"
    _ -> "This will be the result"
end
```

You can also use [Functions](/elixir-fundamentals/functions#guard-clauses) with cases, specifying restrictions on each clause:

```elixir
case {1, 2, 3} do
    {1, 2, 5} -> "This will not return"
    {4, 5, 6} -> "Neither will this"
    {1, x, 3} when x < 3 -> "This will work with any #{x} < 3"
    _ -> "This is used as the 'default' case"
end
```

If none of the clauses match the given value, then an error is raised.

## if

`if` is relatively straightforward and is pretty much the same as the other languages:

```elixir
x = 5
if x > 3 do
    "Greater"
else
    "Lesser"
end
```

There is no explicit `elif` so if you have multiple `if` statements, you will have to nest them as such:

```elixir
if x > 3 do
    "Greater"
else
    if x < 0 do
        "Negative"
    else
        "Lesser"
    end
end
```

Like functions, you can also write the `if` statements as one-liners:

```elixir
if x > 3, do: "Greater", else: "Lesser"
```

## cond

Notice that when using `if`, the lack of an explicit `elif` causes your code to adopt an arrowhead style. This is can make your code look messy. This is where `cond` comes in. `cond` is the same as flattening the nested `if` statements:

```elixir
cond do
    x > 3 -> "Greater"
    x < 0 -> "Negative"
    true -> "Lesser"
end
```

The final `true` clause is used as the "default" case. This is because each clause in `cond` is supposed to evaluate to a boolean.

## unless

A final conditional we are introducing is the `unless` conditional which works as the opposite of `if`. The statement given to `unless` must be false for the body to run.

```elixir
unless true do
    "This will not return"
end
```

## Returning conditionals

Everything in Elixir is an expression. This means that even the conditionals are just expressions. This allows the conditionals to be assigned to variables or returned from functions:

```elixir
x = if y > 3, do: "Greater", else: "Lesser"

def foo do
    if true do
        "This is returned"
    else
        "This isn't"
    end
end
```


# Recursion

While Elixir has a `for` keyword for comprehension, it does not work quite the same as traditional for loops. In fact, Elixir does not have any loop constructs. Thus, all iterative looping has to be done recursively (it is possible to do it through `Enum.reduce/3` but that's out of the scope of this guide).

## Tail-call optimization

Traditionally, recursive calls accrue additional stack frames as the computer has to maintain the state of previous function calls. This causes an increase in memory usage as the stack frames are added till the final base case is met.

However, Elixir uses tail-call optimization. Simply put, the computer only needs to use 1 stack frame to handle all recursive calls so long as the return statement of the function is a recursive call. This means that recursion in Elixir does not incur the same memory cost as other languages.

## Using pattern matching and guard clauses

Recursion is where the powers of pattern matching and guard clauses in functions shine. These mechanisms allow us to design recursive solutions in an incredibly concise manner, moving all base cases to pattern matching or guard clauses where possible.

## Common patterns

This guide will not cover the fundamentals of recursive thinking (you can read [this article for more context](https://en.wikipedia.org/wiki/Recursion)). Instead, it tries to demonstrate how common iterative loops are converted to their recursive counterparts. For demonstration purposes, I will be using Python and Elixir:

### Mapping

{% tabs %}
{% tab title="Python" %}

```python
x = [1, 2, 3]
for i in range(len(n)):
    x[i] *= 2 # doubles all elements in array
print(x)
```

{% endtab %}

{% tab title="Elixir" %}

```elixir
def double_map([], res), do: res
def double_map([xi | rest], res), do: double_map(rest, res ++ [xi * 2])
```

{% endtab %}
{% endtabs %}

### Side effect in loop

{% tabs %}
{% tab title="Python" %}

```python
x = [1, 2, 3]
for i in range(len(x)):
    print(x[i])
```

{% endtab %}

{% tab title="Elixir" %}

```elixir
def print_elements([]), do: nil
def print_elements([xi | rest]) do
    IO.puts(xi)
    print_elements(rest)
end
```

{% endtab %}
{% endtabs %}

### Reducing

{% tabs %}
{% tab title="Python" %}

```python
x = [1, 2, 3, 4]
s = 0
for i in range(len(x)):
    s += x[i]
print(s)
```

{% endtab %}

{% tab title="Elixir" %}

```elixir
def sum([], acc), do: acc
def sum([xi | rest], acc), do: sum(rest, acc + xi)
```

{% endtab %}
{% endtabs %}

### Filtering

{% tabs %}
{% tab title="Python" %}

```python
x = [1, 2, 3, 4, 5]
filtered = []
for i in range(len(x)):
    if x[i] & 1 == 1:
        filtered.append(x[i])
```

{% endtab %}

{% tab title="Elixir" %}

```elixir
def filter([], acc), do: acc
def filter([xi | rest], acc) when Integer.is_even(xi), do: filter(rest, acc)
def filter([xi | rest], acc), do: filter(rest, acc ++ [xi])
```

{% endtab %}
{% endtabs %}

## Using predefined methods

For the common patterns above, the `Enum` module provides functions that encompass the behavior we wish to encode. These will be discussed in the upcoming section.


# Enumerables

Many of the common recursive patterns are provided as functions from the `Enum` module. These are functions that are often chained with one another and reduces the code duplication necessary in your codebase. These patterns often involve operating on "enumerables" (such as lists, tuples, and maps) and produce some result.

## Enum functions

The [`Enum` module](https://hexdocs.pm/elixir/1.12/Enum.html) in Elixir is rather extensive and provides many utility functions that would otherwise require dedicated recursive functions. We will highlight just a few notable ones that you will use most often:

1. `Enum.all?`: returns `true` iff the entire enumerable is `true` or satisfies a given condition based on a given `fun`
2. `Enum.any?`: returns `true` iff any element in the enumerable is `true` or satisfies a given condition based on a given `fun`
3. `Enum.at`: returns the element at a given `index` with a default value if `index` is out of bounds (`nil` by default)
4. `Enum.filter`: returns the filtered enumerable after applying a given predicate
5. `Enum.map`: returns the mapped enumerable after applying a given transformation function
6. `Enum.flat_map`: returns the mapped enumerable after applying a given transformation function and flattens any first-level nested enumerables
7. `Enum.sort`: returns the sorted enumerable

There are many more functions that the `Enum` module provides. Feel free to read the documentation for more information.

## Function chaining

You may notice that applying `Enum` functions (or any function for that matter) often requires chaining, where you pass the output of one function call as the input to another till the final output is produced.

While you can nest these function calls as such:

```elixir
Enum.map(Enum.filter(1..10, fn x -> Integer.is_odd(x) end), fn x -> x * 2 end)
```

It becomes very messy once you have more than two nested function calls. Instead, you can use the pipe operator (`|>`) to perform function chaining (the equivalent of the above example):

```elixir
1..10
|> Enum.filter(fn x -> Integer.is_odd(x) end)
|> Enum.map(fn x -> x * 2 end)
```

Using the pipe operator helps to tidy up the code and reduce the clutter when performing function chaining.

## Streams

`Enum` functions perform computation eagerly, i.e. the transformation function is called for every element of the enumerable immediately in a `map` call. However, this can be incredibly costly as the enumerable might be an infinite stream or just a very large list with the transformation being extremely costly.

Thus, Elixir supports lazy computation of commonly used `Enum` functions through the `Stream` module. This guide will not cover streams in-depth as it is not used in this guide, but it is good to give the [documentation of `Stream`](https://hexdocs.pm/elixir/Stream.html) a read.


# Mix

Mix is Elixir's built-in build tool. Build tools are useful for managing the structure of a project, handling the build process, downloading external packages, and even create custom scripts to handle parts of your project for you. Other build tools include `npm` in Javascript and Gradle in Java.

## Getting started

Mix is a relatively opinionated build tool. This means that Mix provides a project structure for you. This is particularly useful for people starting out so they do not need to think too much about how to structure their projects (compared to languages like Go that have a package management tool but no fixed project structure).

To create a project with Mix, you will use the `mix new` command (should already be installed when you [installed Elixir](/prerequisites)):

```
mix new kv --module KV
```

Mix will create a new folder `kv/` with the following structure:

```
kv/
├── .formatter.exs
├── .gitignore
├── README.md
├── lib
│   └── kv.ex
├── mix.exs
└── test
    ├── kv_test.exs
    └── test_helper.exs
```

## Project structure

The default project structure includes basic files like `README.md` and `.gitignore` and additional Elixir specific files and folders.

### lib/

The `lib/` folder contains the code that you will write. Notice that when you created the project, you specified the module name as `KV`. This should be the root module of your project.

The initial file `lib/kv.ex` will be the root file that contains the `KV` module and any related functions under this module. If you are creating any sub-modules, you will create a folder `lib/kv/` and add the folders/files there.

```
lib/
├── kv
│   └── utils
│       └── utils.ex
└── kv.ex
```

Where `utils.ex` looks like this:

```elixir
defmodule KV.Utils do
  def foo do
    :foo
  end
end
```

### test/

Mix uses [ExUnit](https://hexdocs.pm/ex_unit/ExUnit.html) (Elixir's testing framework) to perform unit testing. These unit tests are stored under the `test/` folder and the folder follows the same structure as the `lib/` folder. Test files are appended with `_test` and run as `.exs` files instead of `.ex`.&#x20;

You can run the tests via

```
mix test
```

This guide will not go into the full details of unit testing in Elixir.

### mix.exs

This is the equivalent of `package.json` or `build.gradle` where it contains the specific instructions and packages to include for the project.

```elixir
defmodule KV.MixProject do
  use Mix.Project

  def project do
    [
      app: :kv,
      version: "0.1.0",
      elixir: "~> 1.16",
      start_permanent: Mix.env() == :prod,
      deps: deps()
    ]
  end

  # Run "mix help compile.app" to learn about applications.
  def application do
    [
      extra_applications: [:logger]
    ]
  end

  # Run "mix help deps" to learn about dependencies.
  defp deps do
    [
      # {:dep_from_hexpm, "~> 0.3.0"},
      # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
    ]
  end
end
```

For now, the key function you should be concerned about is `deps` which specifies the dependencies of the project. The packages of the project are specified as a list of tuple.

## Compiling the project

Previously in [Modules](/elixir-fundamentals/modules#compiling-files-with-modules), you will notice that it is quite tedious to compile each file yourself and it can be quite messy with the `.beam` files scattered across the folder.

Mix fixes this by helping us with the compilation process:

```
mix compile
```

Then, to run IEx with the compiled files, you can use:

```
iex -S mix
```

## Creating a long running application

You may not want your application to only be runnable from IEx. If you wish to create a long running application, you will have to use the [`Application` module](https://hexdocs.pm/elixir/1.13/Application.html).

First, create a file `application.ex` under `lib/kv/` and add the following contents to the file:

```elixir
defmodule KV.Application do
  use Application

  def start(_type, _args) do
    IO.puts("Hello world!")
    children = []
    Supervisor.start_link(children, strategy: :one_for_one)
  end
end

```

Then, go to `mix.exs` and add the following line to the array returned by the `application` function:

```elixir
def application do
  [
    # ...
    mod: {KV.Application, []}
  ]
end
```

This allows Mix to know to run the `start/2` behavior under `KV.Application` module when you use:

```
mix run
```

You should see `Hello World` printed as specified by `start/2`.


# Web development with Elixir

Web development has become a growing area that Elixir has begun to shine. This is in part thanks to the [Phoenix framework](https://www.phoenixframework.org/) created by [Chris McCord](https://chrismccord.com/) that has made web development very hassle free with Elixir.

{% hint style="info" %}
[**Heroku x Elixir**](https://elixir-lang.org/blog/2020/09/24/paas-with-elixir-at-Heroku/)\
\
Heroku was one of the companies that adopted Elixir and Phoenix for web development of one of their core analytics dashboard and internal systems. What they had noticed from using Elixir was that their systems were able to handle higher loads while maintaining a high response time.
{% endhint %}

## About Phoenix

Phoenix is a server-side framework designed for building web applications with Elixir. It was first created by Chris McCord and modeled after the [Ruby on Rails](https://rubyonrails.org/) framework.

It is a server-side framework, which means that all of the computation is performed on the server and sent to the client to render. Phoenix implements the Model-View-Controller (MVC) pattern:

<figure><img src="/files/HpgkqDPj4tn6Lglsazco" alt="" width="311"><figcaption></figcaption></figure>

You can build both your frontend and backend with Phoenix. The frontend of Phoenix is written as `.heex` files which is Phoenix's template language that embeds Elixir into HTML. The backend of Phoenix uses [Plug](https://hexdocs.pm/plug/readme.html).

## What this guide covers

As mentioned in the beginning of the guide, this guide does not attempt to provide a comprehensive introduction to the Phoenix framework. There are many nuances to how Phoenix can be used and you should refer to the official documentation if you are [interested in learning more about Phoenix](https://hexdocs.pm/phoenix/up_and_running.html).

This guide will provide an introduction to Phoenix by getting you to build a to-do list application with some basic data persistence using SQLite3 (we are using a local database to reduce the amount of setup required to following along).

This guide will cover both base Phoenix and Phoenix LiveView. The reason for covering base Phoenix is to provide a firm foundation on the terminology before discussing slightly more advanced concepts in LiveView.

### Structure

The following is the content structure of this guide:

1. Directory structure of Phoenix projects
2. Request lifecycle
3. Representing to-do items in Elixir
4. Anatomy of routers
5. Creating a new HTTP endpoint
6. Rendering the to-do list
7. Introduction to LiveView
8. Adding more actions to to-do list
9. Persisting data with Ecto and SQLite3

{% hint style="info" %}
If you are watching the recorded series or attending the live event, the last part of persisting data may not be covered depending on time constraint. Do give the chapter a read if you have time as data persistence is a big component of working with web applications!
{% endhint %}

## Getting started

To get started with using web development using Elixir and Phoenix, we will start by installing the Phoenix CLI archive on your machine:

```
mix archive.install hex phx_new
```

Then, you can create a new Phoenix project with the necessary boilerplate:

```
mix phx.new practical_elixir_demo --database sqlite3
```

You may be prompted on whether you want to fetch the dependencies, agree to that option.

Then, navigate to the new folder:

```
cd practical_elixir_demo/
```

Then, create the database. Phoenix uses a very popular Elixir library, Ecto for performing database queries and mapping:

```
mix ecto.create
```

Finally, you can run the demo application:

```
mix phx.server
```

You should see the following show up at [http://localhost:4000](http://localhost:4000/)

<figure><img src="/files/UZuNZ7lUZoraFSpVqDzi" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
If you want to view the full codebase without running each command, refer to [this repository instead.](https://github.com/woojiahao/practical_elixir_demo)
{% endhint %}


# Directory structure

If you had run the getting started instructions from the previous page, you would see the following directory structure for your project:

```
.
├── .formatter.exs
├── .gitignore
├── README.md
├── _build
├── assets
│   ├── css
│   ├── js
│   ├── tailwind.config.js
│   └── vendor
├── config
│   ├── config.exs
│   ├── dev.exs
│   ├── prod.exs
│   ├── runtime.exs
│   └── test.exs
├── lib
│   ├── practical_elixir_demo
│   ├── practical_elixir_demo.ex
│   ├── practical_elixir_demo_web
│   └── practical_elixir_demo_web.ex
├── mix.exs
├── mix.lock
├── practical_elixir_demo_dev.db
├── practical_elixir_demo_dev.db-shm
├── practical_elixir_demo_dev.db-wal
├── priv
│   ├── gettext
│   ├── repo
│   └── static
└── test
    ├── practical_elixir_demo_web
    ├── support
    └── test_helper.exs
```

Phoenix builds on top of the original folder structure that was discussed under the Mix section: [Mix](/elixir-fundamentals/mix#project-structure). For more information about Phoenix's directory structure, refer to the [official guide.](https://hexdocs.pm/phoenix/directory_structure.html)

1. `_build`: contains the compilation artifacts
2. `assets`: stores front-end assets like JavaScript and CSS; static assets like files go to `priv/static`
3. `config`: common directory used to store project configurations (more information about order of config reading below)
4. `deps`: contains all Mix dependencies listed under `mix.exs` under `deps/0`
5. `lib`: umbrella project for main application source code where `lib/practical_elixir_demo` holds the back-end logic (model) while `lib/practical_elixir_demo_web` holds the front-end logic (view and controller)
6. `priv`: all resources that are necessary in production but not a part of source code like database scripts, static resources, etc.
7. `test`: contains unit tests for application, similar structure as `lib`

The specific structure of each folder in `lib/` will be discussed when we actually modify those files.

The file that end with `*.db-*` are the SQLite3 files that maintain the database.

## Config order

There are several configuration files that are used in Phoenix (or any Mix project for that matter) and in their read order:

1. `config.exs`: base configurations declared for the project, these are read during compile time
2. `dev.exs`/`prod.exs`/`test.exs`: environment specific configurations that run only when the project is run in a given environment (during compile time as well)
3. `runtime.exs`: configurations that are run during runtime

`runtime.exs` are the preferred method for loading environment variables from environment files like `.env` while `config.exs` are preferred for environment variables that may not be secrets and are readily accessible during compile time.

For more information about configuration files in Elixir, please refer to the official [Elixir documentation for the `Config` module](https://hexdocs.pm/elixir/main/Config.html).


# First steps

## Request lifecycle

Phoenix (not LiveView, just regular Phoenix) relies on the typical HTTP request lifecycle that many other web applications use:

{% @mermaid/diagram content="sequenceDiagram
actor User
participant Client
participant Server
participant Database
User ->> Client : perform action
Client ->> Server : HTTP request
Server ->> Database : fetch information
Database -->> Server : information
Server -->> Client : HTTP response
Client -->> User : render result of action" %}

## Basics of HTTP

HyperText Transfer Protocol or HTTP is an application-layer network protocol designed to transfer data between networked devices and is run on top of the TCP protocol.

If all of that seemed like a bunch of word soup, don't worry, all it means is that HTTP is the medium through which clients and servers will communicate with one another.

HTTP requests are primarily composed of the following:

1. Verb: what kind of action should be done with this request; GET, PUT, POST, DELETE
2. Path: what the endpoint of this request is
3. Body: additional contents of the request, usually stored as JSON
4. Query parameters: following a `?` at the end of the path, key-value pairs delimited by `&`

With the very fundamentals of HTTP out of the way, let us start with implementing a new page for our to-do list application.

## Representing to-do items&#x20;

The first thing we would like to do is to first define how a note would be represented in our application. Although we will be integrating SQLite3 to persist our data, let us first try representing the to-do items in a purely Elixir method: using [structures](https://hexdocs.pm/elixir/structs.html).

Structures are an extension over [Keyword lists and maps](/elixir-fundamentals/types/keyword-lists-and-maps#maps) that provide additional compile-time checks and default values that maps do not provide directly.

We can define a new struct the same way we define methods and modules, using the `defstruct` macro.&#x20;

First, create a new folder under `lib/practical_elixir_demo/` and title it `todo/`. Then, add a new file called `todo_item.ex` which is where we will declare our struct:

{% code title="lib/practical\_elixir\_demo/todo/todo\_item.ex" %}

```elixir
defmodule PracticalElixirDemo.Todo.TodoItem do
  @derive Jason.Encoder
  defstruct [:title, description: nil, is_done?: false]
end
```

{% endcode %}

For this guide, we will keep things really simple and only include three fields for our to-do items. We added a module attribute `@derive` to inform Phoenix that the `TodoItem` struct should be encoded to a JSON form. This is useful when we're sending data from the server to the client.

We can also create a few dummy to-do items to get started. We can store those under `lib/practical_elixir_demo/todo/todo.ex`:

{% code title="lib/practical\_elixir\_demo/todo/todo.ex" %}

```elixir
defmodule PracticalElixirDemo.Todo do
  alias PracticalElixirDemo.Todo.TodoItem

  def get_items do
    [
      %TodoItem{
        title: "Finish homework"
      },
      %TodoItem{
        title: "Find accommodation",
        description: "Ideally somewhere that is cheap and quiet"
      },
      %TodoItem{
        title: "Take out the rubbish",
        is_done?: true
      }
    ]
  end
end
```

{% endcode %}

{% hint style="info" %}
`alias` is used so we can reference the `TodoItem` struct without using the full module name.
{% endhint %}

Now that we have created our to-do item struct, we can now start to think about working with these constructs, such as returning these structs as HTTP responses and rendering them on the front-end.


# Phoenix basics

To first render the to-dos, let's first create a new endpoint to handle this "retrieval" request (i.e. `GET` request) and add a new page to render this content.

## Anatomy of the router

Phoenix uses a router to map HTTP routes to actions (functions) handled by a controller (from the MVC pattern). You define both the front-end and back-end routes via the router.

New routes are added to the `lib/practical_elixir_demo_web/router.ex` file. If you open this file, you will see the following:

{% code title="lib/practical\_elixir\_demo\_web/router.ex" %}

```elixir
defmodule PracticalElixirDemoWeb.Router do
  use PracticalElixirDemoWeb, :router

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_live_flash
    plug :put_root_layout, html: {PracticalElixirDemoWeb.Layouts, :root}
    plug :protect_from_forgery
    plug :put_secure_browser_headers
  end

  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/", PracticalElixirDemoWeb do
    pipe_through :browser

    get "/", PageController, :home
  end

  # Other scopes may use custom stacks.
  # scope "/api", PracticalElixirDemoWeb do
  #   pipe_through :api
  # end

  # Enable LiveDashboard and Swoosh mailbox preview in development
  if Application.compile_env(:practical_elixir_demo, :dev_routes) do
    # If you want to use the LiveDashboard in production, you should put
    # it behind authentication and allow only admins to access it.
    # If your application does not have an admins-only section yet,
    # you can use Plug.BasicAuth to set up some basic authentication
    # as long as you are also using SSL (which you should anyway).
    import Phoenix.LiveDashboard.Router

    scope "/dev" do
      pipe_through :browser

      live_dashboard "/dashboard", metrics: PracticalElixirDemoWeb.Telemetry
      forward "/mailbox", Plug.Swoosh.MailboxPreview
    end
  end
end
```

{% endcode %}

### use

```elixir
  use PracticalElixirDemoWeb, :router
```

This is "similar" to inheritance in OOP (but not really) where macros are used to initialize several predefined behaviors for the current router module to behave like a router.

### Pipelines

```elixir
  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_live_flash
    plug :put_root_layout, html: {PracticalElixirDemoWeb.Layouts, :root}
    plug :protect_from_forgery
    plug :put_secure_browser_headers
  end

  pipeline :api do
    plug :accepts, ["json"]
  end
```

Pipelines are comprised of defined "plugs" (this is the name of the library) which defines some kind of behavior. Any requests that are routed through a pipeline will have to pass through these checks before they reach the controller's action. These are useful when you want to modify the request or perform validation on the request before it is even executed.

### Scopes

```elixir
  scope "/", PracticalElixirDemoWeb do
    pipe_through :browser

    get "/", PageController, :home
  end
```

`scope` defines the parent route name and the module that would contain the controllers with the actions.

In the above example, we define the parent route to be `/` and the parent module that contains the controllers as the `PracticalElixirDemoWeb` module. You are free to use a different module to store these controllers but this guide will follow the convention and define the controllers under the `lib/practical_elixir_demo_web/` folder.

## Retrieving the to-do list

To familiarize yourself with the router structure, we can define an API endpoint that returns the dummy list of to-do items.

{% hint style="warning" %}
This guide will not actually require any API endpoints as we would be able to retrieve all of the information directly through the back-end (more on how this will work later). \
\
However, in projects that have separate repositories for the front-end and back-end, you would use these API endpoints to query for the information!
{% endhint %}

Add the following lines to the router:

```elixir
  scope "/api", PracticalElixirDemoWeb do
    pipe_through :api

    get "/todo", TodoController, :get_todo_list
  end
```

And then add a file under `lib/practical_elixir_demo_web/controllers/` called `TodoController.ex`. Then add the following to the new controller:

```elixir
defmodule PracticalElixirDemoWeb.TodoController do
  use PracticalElixirDemoWeb, :controller

  def get_todo_list(conn, _params) do
    json(conn, PracticalElixirDemo.Todo.get_items())
  end
end
```

We've basically done the following:

1. Create a new route `GET /api/todo`
2. Setup the router to call `TodoController.get_todo_list/0` when the route is called
3. `get_todo_list/0` calls the function to get all to-do items `Todo.get_items/0`&#x20;
4. Returns the resulting to-do list as a JSON using the `json/2` function

Thus, when you run the web application again, you can call this endpoint using a program like cURL or just using the URL in the browser:

```
curl localhost:4000/api/todo
```

```json
[{"description":null,"title":"Finish homework","is_done?":false},{"description":"Ideally somewhere that is cheap and quiet","title":"Find accommodation","is_done?":false},{"description":null,"title":"Take out the rubbish","is_done?":true}]%         
```

As you can see, the newly created endpoint returns the dummy list of to-dos we have.


# Viewing to-do list

## Creating a new route

Similar to how we have created the `GET /api/todo` endpoint, we will first create the route in `router.ex`. This time, add it to the existing `scope "/"` that was given.

```elixir
  scope "/", PracticalElixirDemoWeb do
    # ...
    get "/todo", PageController, :todo
  end
```

## Creating the associated controller action

Once done, we need to add the associated controller action `todo` in the `PageController`. We will disable the default layout as we want to style the web page ourselves.

```elixir
defmodule PracticalElixirDemoWeb.PageController do
  # ...

  def todo(conn, _params) do
    render(
      conn,
      :todo,
      layout: false,
      todo_list: PracticalElixirDemo.Todo.get_items()
    )
  end
end

```

Notice that we directly retrieve the information needed for rendering to the to-do list and pass it as a keyword to the `render/3` function. This way, we are able to use this information directly in the page.

## Introducing HEEx (HTML + EEx)

Finally, add a file to `lib/practical_elixir_demo_web/controllers/page_html/` titled `todo.html.heex`. Note that the filename must correspond to the atom specified in the `render/2` function call, i.e. `todo`.

{% hint style="info" %}
Note that this differs from the original method proposed by the Phoenix framework (which involves using the `~H` sigil to render the HTML. This is method is known as using "template files".
{% endhint %}

Now that we have the `todo.html.heex` file, we can design the to-do list view of our application.

{% hint style="info" %}
HEEx is a templating language designed with work with embedding Elixir in HTML. It is very similar to how Vue.js and Django handles embedding (where you can write Javascript/Python inside HTML).
{% endhint %}

Then, you can populate the file with the following:

{% code title="" %}

```html
<div>
  <h1>Todo List</h1>
  <%= for item <- @todo_list do %>
    <%= if item.is_done? do %>
      <p>✅</p>
    <% else %>
      <p>❌</p>
    <% end %>
    <p>
      <%= item.title %>
    </p>
    <p>
      <%= item.description %>
    </p>
  <% end %>
</div>

```

{% endcode %}

Then, if you ran the web application and navigated to <http://localhost:4000/todo>, you should notice the following being rendered:

<figure><img src="/files/sJLEtDsVcutRZ8UgdnQO" alt=""><figcaption></figcaption></figure>

Nothing too fancy, but as you can see, the dummy to-do list we have constructed earlier now appears on the page.

With this, we can introduce several other concepts in Phoenix and HEEx.&#x20;

### Evaluating expressions

You may have noticed the unique `<%= %>` syntax used in the code above. What it effectively does is executes the expression within the block and inserts the results into the page. You can try it out by adding a line like `<p><%= 5 + 5 %></p>` and it will render to the front-end as `10`.

You may also use `<% %>` (without `=`) if your expression does not return any values or you do not wish to output the return value. As such, we can replace `<%= else %>` with `<% else %>` too!

### Reading values from controller

Notice that previously, we had passed a `todo_list` keyword into the `render/3` function in the `page_controller.ex` file. We have accessed this variable from the view via the `@todo_list` syntax.

This is a nifty way to access variables passed to the view via the controller.&#x20;

### Functional components

Functional components serve as a shared abstraction for commonly used UI that can be reused across views and layouts. They are not a new concept, and can often be seen in other front-end libraries like React.

For our demo, let's abstract each to-do list item to its own functional component so that any modifications made to one will be uniform across all.

To declare a new functional component for that view controller, all you need to do is to add a function with the function name as the name of the intended functional component and it must receive an argument called `assigns`. The purpose of this argument is to allow the HEEx to pass data to the functional component to be used within the functional component:

{% code title="lib/practical\_elixir\_demo\_web/controllers/page\_html.ex" %}

```elixir
defmodule PracticalElixirDemoWeb.PageHTML do
  # ...

  def todo_item(assigns) do
    ~H"""
    <%= if @item.is_done? do %>
      <p>✅</p>
    <% else %>
      <p>❌</p>
    <% end %>
    <p>
      <%= @item.title %>
    </p>
    <p>
      <%= @item.description %>
    </p>
    """
  end
end

```

{% endcode %}

You use the `@<variable>` notation to access values passed to the functional component via `assigns`. Also notice that we have effectively moved the entire block found in the `for` loop into the functional component.

Then, you can use the functional component in the HEEx as follows:

{% code title="lib/practical\_elixir\_demo\_web/controllers/page\_html/todo.html.heex" %}

```html
<div>
  <h1>Todo List</h1>
  <%= for item <- @todo_list do %>
    <.todo_item item={item} />
  <% end %>
</div>
```

{% endcode %}

We have replaced the bulk of the `for` loop body with the functional component which is referenced via the `.<functional_component>` notation with the `@item` given as an a HTML attribute. Phoenix intelligently does the mapping from HEEx to functional component.

### Styling with Phoenix

The current to-do list view is quite plain. Let's spruce things up a bit.

As mentioned in [Directory structure](/web-development-with-elixir/directory-structure), all CSS files are stored under `assets/`. However, if you opened the `assets/css/app.css` file, you will notice that there are the following three lines:

{% code title="assets/css/app.css" %}

```css
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
```

{% endcode %}

Phoenix uses [Tailwind CSS](https://tailwindcss.com/) under the hood for performing styling. This means that rather than writing CSS files and storing them under `assets/css` you can actually write inline styles for each element as per Tailwind CSS, very handy!

So let's get to styling our web application. We will not cover how CSS styling with Tailwind works because that's out of the scope of this guide.

{% hint style="info" %}
You can apply Tailwind styles to both view template files and functional components.
{% endhint %}

{% code title="lib/practical\_elixir\_demo\_web/controllers/page\_html/todo.html.heex" %}

```html
<div class="w-[40%] mx-auto my-8">
  <h1 class="font-bold text-3xl my-8 py-4 px-4 bg-slate-100">Todo List</h1>
  <%= for item <- @todo_list do %>
    <.todo_item item={item} />
  <% end %>
</div>
```

{% endcode %}

{% code title="lib/practical\_elixir\_demo\_web/controllers/page\_html.ex" %}

```elixir
defmodule PracticalElixirDemoWeb.PageHTML do
  # ...

  def todo_item(assigns) do
    ~H"""
    <div class="flex gap-x-4 mb-4 last:mb-0">
      <%= if @item.is_done? do %>
        <p>✅</p>
      <% else %>
        <p>❌</p>
      <% end %>
      <div>
        <p>
          <%= @item.title %>
        </p>
        <p class="italic">
          <%= @item.description %>
        </p>
      </div>
    </div>
    """
  end
end

```

{% endcode %}

These styles should result in the following page:

<figure><img src="/files/MQ0WK8eMj6WRgUFN0ft0" alt=""><figcaption></figcaption></figure>

Now that the to-do list is more visually appealing, let's dive into setting up dynamic behavior.


# Adding dynamic behavior

You may notice that viewing the to-do list may be nice, but it is sorely lacking the key features of a to-do application like creating a new to-do and marking a to-do as done.

If you have dug around with the original Phoenix documentation, you will notice that there is no mention on how this can be achieved. This is because in the base version of Phoenix, handling button actions and performing such dynamic changes requires the use of JavaScript. This is not uncommon for most server-side frameworks.

> "But I don't want to use JavaScript!"\
> \- Presumably you

This is where **Phoenix LiveView** comes in. Phoenix LiveView is an "add-on" on top of base Phoenix that provides real-time server-rendered HTML.

{% hint style="danger" %}
The following content covered will require a bit of time to grasp as it is a relatively new concept introduced over this guide. Do take your time when reading and understanding.
{% endhint %}

## Adding buttons

Before we dive into LiveView, there are some changes to the design that we will add such as buttons for the actions and a task bar to add new to-do items.

Replace the `todo.html.heex` file with:

```html
<div class="w-[40%] mx-auto my-8">
  <h1 class="font-bold text-3xl my-8 py-4 px-4 bg-slate-100">Todo List</h1>
  <form phx-submit="add-todo" class="flex justify-between items-center mb-8">
    <input type="text" placeholder="New task" name="task-name" class="rounded-md w-full" />
    <button type="submit" class="px-4 py-2 bg-green-100 rounded-md ml-2">
      Add
    </button>
  </form>
  <%= for item <- @todo_list do %>
    <.todo_item item={item} />
  <% end %>
</div>
```

And edit the `todo_item` functional component:

```html
    <div class="flex gap-x-4 mb-4 last:mb-0 items-center">
      <%= if @item.is_done? do %>
        <p>✅</p>
      <% else %>
        <p>❌</p>
      <% end %>
      <div class="w-full">
        <div class="flex justify-between items-center w-full">
          <p>
            <%= @item.title %>
          </p>

          <div>
            <%= if @item.is_done? do %>
              <button class="bg-blue-300 px-3 py-1 font-bold rounded-md text-sm">
                Mark as Not Done
              </button>
            <% else %>
              <button class="bg-green-300 px-3 py-1 font-bold rounded-md text-sm">
                Mark as Done
              </button>
            <% end %>
            <button class="bg-yellow-300 px-3 py-1 font-bold rounded-md text-sm">Edit</button>
            <button class="bg-red-300 px-3 py-1 font-bold rounded-md text-sm">Delete</button>
          </div>
        </div>
        <p class="italic">
          <%= @item.description %>
        </p>
      </div>
    </div>
```

You should see the following UI once done:

<figure><img src="/files/o2eo7sGi07kjI69P8vRV" alt=""><figcaption></figcaption></figure>

## Introducing LiveView

Phoenix LiveView inverts the traditional request-response lifecycle that we have seen so far. Instead of sending a request to the API/server and re-rendering the front-end based on the response, LiveView first uses a regular HTTP request-response to retrieve the initial page.&#x20;

The key benefit from doing this is reducing the amount of time spent waiting for HTTP responses in a traditionally stateless application.

However, once the page is rendered, a persistent connection (via a socket) is established between client and server and this connection is used to communicate any changes/actions performed from the front-end to the back-end and vice versa.

{% @mermaid/diagram content="sequenceDiagram
actor User
participant Client
participant Server
participant Database
User ->> Client : request page
Client ->> Server : request page over HTTP
Server ->> Database : initial data request
Database -->> Server : initial data
Server -->> Client : initial page as HTTP response
Client -->> User : render initial page
note over Client,Server : all subsequent requests are handled via a<br/>persistent connection between<br/>Client and Server " %}

This gives rise to the following lifecycle for LiveView (courtesy of [John Elm Labs](https://johnelmlabs.com/posts/liveview-lifecycle-flow-chart) for this diagram):

{% @mermaid/diagram content="graph TB
HTTP\_Request\["HTTP Request"]
mount\_disconnected\["mount/3 Callback (Disconnected)"]
handle\_params\_disconnected\["handle\_params/3 Callback (Disconnected)"]
render\_disconnected\["render/1 Callback (Disconnected)"]
LiveView\_Connects\["LiveView Connects (Stateful views are spawned)"]
mount\_connected\["mount/3 Callback (Connected)"]
handle\_params\_connected\["handle\_params/3 Callback (Connected)"]
render\_connected\["render/1 Callback (Connected)"]
Continuous\_Connection\["Continuous Connection"]
handle\_event\["Callbacks\nhandle\_event/3\nhandle\_call/3\nhandle\_info/2\nhandle\_continue/2\nhandle\_cast/2\n"]
Reconnect\["Reconnect"]
terminate\["Terminate callback - handle cleanup"]

```
HTTP_Request --> mount_disconnected
mount_disconnected --> handle_params_disconnected
handle_params_disconnected --> render_disconnected
render_disconnected --> LiveView_Connects
LiveView_Connects --> mount_connected
mount_connected --> handle_params_connected
handle_params_connected --> render_connected
render_connected --> Continuous_Connection
Continuous_Connection --> handle_event
handle_event --> render_connected
Continuous_Connection -- "If crash or connection drop" --> Reconnect
Reconnect --> mount_connected
Continuous_Connection -- "Patch" --> handle_params_connected

classDef orange fill:#f96,stroke:#333;
class mount_connected orange;
class Continuous_Connection orange;
class handle_event orange;" %}
```

It may seem like a handful but we will only be focusing on the components colored orange as they are the most fundamental ideas of LiveView.&#x20;

## Getting started with LiveView

{% hint style="info" %}
If you wish to get the converted version of the LiveView application immediately without running through all of the steps to convert the current demo application to LiveView, you can pull the branch `liveview-base` from the GitHub repository and get started.\
\
The full demo application is found on the `liveview` branch.\
\
Otherwise, you can expand the section below to learn how to convert a base Phoenix project to use LiveView.
{% endhint %}

<details>

<summary>Migrating base Phoenix to Phoenix LiveView</summary>

A great point of reference is this [guide](https://smartlogic.io/blog/converting-phoenix-views-to-liveviews/) following steps 1 to 5. However, to explicitly state the steps needed:

* Replace `get "/todo", PageController, :todo` with `live "/todo", TodoLive`
* Add a new module `PracticalElixirDemoWeb.TodoLive` under `practical_elixir_demo_web/live/todo_live.ex` with the following:

{% code title="lib/practical\_elixir\_demo\_web/live/todo\_live.ex" %}

```elixir
defmodule PracticalElixirDemoWeb.TodoLive do
  use PracticalElixirDemoWeb, :live_view
end
```

{% endcode %}

* Create a new file: `lib/practical_elixir_demo_web/live/todo_live.html.heex` (note that the base name must be the same as the module file name)

{% code title="lib/practical\_elixir\_demo\_web/live/todo\_live.html.heex" %}

```html
<div class="w-[40%] mx-auto my-8">
  <h1 class="font-bold text-3xl my-8 py-4 px-4 bg-slate-100">Todo List</h1>
  <div class="flex justify-between items-center mb-8">
    <input type="text" placeholder="New task" class="rounded-md w-full" />
    <button type="button" class="px-4 py-2 bg-green-100 rounded-md ml-2">Add</button>
  </div>
  <%= for item <- @todo_list do %>
    <.todo_item item={item} />
  <% end %>
</div>
```

{% endcode %}

* Populate `TodoLive` with the `mount/3` function and copy over the `todo_item/1` function from the old view

{% code title="lib/practical\_elixir\_demo\_web/live/todo\_live.ex" %}

```elixir
defmodule PracticalElixirDemoWeb.TodoLive do
  alias PracticalElixirDemo.Todo
  use PracticalElixirDemoWeb, :live_view

  def mount(_params, _session, socket) do
    socket = assign(socket, todo_list: Todo.get_items())
    {:ok, socket, layout: false}
  end

  def todo_item(assigns) do
    ~H"""
    <div class="flex gap-x-4 mb-4 last:mb-0 items-center">
      <%= if @item.is_done? do %>
        <p>✅</p>
      <% else %>
        <p>❌</p>
      <% end %>
      <div class="w-full">
        <div class="flex justify-between items-center w-full">
          <p>
            <%= @item.title %>
          </p>

          <div>
            <%= if @item.is_done? do %>
              <button class="bg-blue-300 px-3 py-1 font-bold rounded-md text-sm">Mark as Done</button>
            <% else %>
              <button class="bg-green-300 px-3 py-1 font-bold rounded-md text-sm">
                Mark as Not Done
              </button>
            <% end %>
            <button class="bg-yellow-300 px-3 py-1 font-bold rounded-md text-sm">Edit</button>
            <button class="bg-red-300 px-3 py-1 font-bold rounded-md text-sm">Delete</button>
          </div>
        </div>
        <p class="italic">
          <%= @item.description %>
        </p>
      </div>
    </div>
    """
  end
end
```

{% endcode %}

* Re-run your application and go to <http://localhost:4000>, you should still see the same application

</details>

Whether you have pulled the branch `liveview-base` or followed the guide in the expandable above, you will notice that there are several changes to the structure of the project:

1. Adding `lib/practical_elixir_demo_web/live/` with `todo_live.ex` and `todo_live.html.heex`: these are just to create a logical separation between LiveView and base Phoenix, though most projects will adopt a similar separation
2. Converting `get "/todo"...` with `live "/todo"`: this is a key part of declaring the `/todo` route as being a LiveView; we can omit the specific controller action as the entire LiveView will be treated as the controller + view
3. Declaring the data retrieval in `mount/3` of `todo_live.ex`: as per the new lifecycle of LiveView, the `mount/3` function is called before the page is rendered so we will use it to load the initial data (i.e. to-do list)

   You may also notice that we call `assign` in the `mount/3` function while passing the socket and a keyword list of values. `assign` is used to persist the state of the page in the persistent connection (socket). It is what allows us to interact with the client without having to use HTTP calls!

Everything else is the same as the previous code we had written! That's one of the biggest draws of Phoenix LiveView - a high degree of interoperability with base Phoenix!

## Creating new to-dos

Let's make the "Add" button functional. First, notice that we have defined a `form` element in `todo_live.html.heex` and within the `form` element, we have included an attribute: `phx-submit`. This creates a [binding](https://hexdocs.pm/phoenix_live_view/welcome.html#bindings) between the client and server.&#x20;

{% code title="lib/practical\_elixir\_demo\_web/live/todo\_live.html.heex" %}

```html
  <form phx-submit="add-todo" class="flex justify-between items-center mb-8">
    <input type="text" placeholder="New task" name="task-name" class="rounded-md w-full" />
    <button type="submit" class="px-4 py-2 bg-green-100 rounded-md ml-2">
      Add
    </button>
  </form>
```

{% endcode %}

What this binding does is inform Phoenix that on form submission (via the button in the form), Phoenix should trigger the given event. Then, you must specify the appropriate event handler in the LiveView controller:

{% code title="lib/practical\_elixir\_demo\_web/live/todo\_live.ex" %}

```elixir
  def handle_event("add-todo", value, socket) do
    {:noreply, socket}
  end
```

{% endcode %}

As highlighted previously (in [#introducing-liveview](#introducing-liveview "mention")), [`handle_event/3`](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#c:handle_event/3) is one of many callbacks that the server uses to handle when a client event is triggered. The event is comprised of three parameters:

1. Event name: name of the event raised by the client (this is the name that is given to `phx-submit`)
2. Event payload: values given to the server from the client, usually the form data
3. Existing socket: represents the persistent connection, updating the data stored in this socket informs the client to update its view to render the latest information as well

With this understanding, we can now implement the to-do item creation logic.

The first thing we want to do is to retrieve the existing list of to-dos from the socket. This can be done using [Pattern matching](/elixir-fundamentals/pattern-matching) since both the the `value` and `socket` are maps.

{% code title="lib/practical\_elixir\_demo\_web/live/todo\_live.ex" %}

```elixir
  def handle_event(
        "add-todo",
        %{"task-name" => task_name},
        %Phoenix.LiveView.Socket{assigns: %{todo_list: todo_list}} = socket
      ) do
    {:noreply, socket}
  end
```

{% endcode %}

{% hint style="info" %}
In this demonstration, we have opted to keep things simple and remove the use of the to-do description field. However, if you wish to extend the application, feel free to add support for filling in that field as well!
{% endhint %}

Then, we can update the to-do list with the new task and submit the update to the client via the socket.

{% code title="lib/practical\_elixir\_demo\_web/live/todo\_live.ex" %}

```elixir
  def handle_event(
        "add-todo",
        %{"task-name" => task_name},
        %Phoenix.LiveView.Socket{assigns: %{todo_list: todo_list}} = socket
      ) do
    new_todo_list = [%TodoItem{title: task_name}] ++ todo_list
    {:noreply, assign(socket, todo_list: new_todo_list)}
  end
```

{% endcode %}

We're literally just adding a new `TodoItem` to the front of the `todo_list` and re-assigning it to the socket. When you try out this interaction on your application, you will see the new task added to the very top of the to-do list.

Therein lies one of the strengths of Phoenix LiveView: the ability to use handle client-server interaction directly as Elixir functions, rather than HTTP calls.

{% hint style="info" %}
`:noreply` tells the client that there is no additional information to be sent to the client. `:reply` exists to indicate that there is some reply to be sent to the client.\
\
For more information, refer to the documentation of [`handle_event/3`](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#c:handle_event/3)
{% endhint %}

## Marking to-dos as done

Another common action in a to-do application would be to mark your tasks as done. We can implement such logic like we have for the previous feature. However, this time, we will need to update several fields of the HEEx file to make this a reality.

The most important step is to identify the relative position of each to-do item within the list. This can be done by altering how we generate each to-do item, adding an `id` to each of them representing their unique position:

{% code title="lib/practical\_elixir\_demo\_web/live/todo\_live.html.heex" %}

```html
  <%= for {item, id} <- Enum.with_index(@todo_list) do %>
    <.todo_item item={item} id={id} />
  <% end %>
```

{% endcode %}

We use a helper function `Enum.with_index/2` to include the relative index of each to-do item. Then, we pass the `id` to the `todo_item` functional component. In the controller, we need to also update the functional component to make use of this `id`:

{% code title="lib/practical\_elixir\_demo\_web/live/todo\_live.ex" %}

```html
            <%= if @item.is_done? do %>
              <button
                class="bg-blue-300 px-3 py-1 font-bold rounded-md text-sm"
                type="button"
                phx-value-id={@id}
              >
                Mark as Not Done
              </button>
            <% else %>
              <button
                class="bg-green-300 px-3 py-1 font-bold rounded-md text-sm"
                type="button"
                phx-value-id={@id}
              >
                Mark as Done
              </button>
            <% end %>
```

{% endcode %}

We use the `phx-value-*` binding to specify this as a parameter. This means that when we perform an event on the button, this value would be sent as part of the `values` map that the `handle_event/3` callback receives.

Speaking of which, we should also add a binding to handle the button clicks as well. We use the `phx-click` binding for this:

```html
            <%= if @item.is_done? do %>
              <button
                class="bg-blue-300 px-3 py-1 font-bold rounded-md text-sm"
                type="button"
                phx-value-id={@id}
                phx-click="mark-not-done"
              >
                Mark as Not Done
              </button>
            <% else %>
              <button
                class="bg-green-300 px-3 py-1 font-bold rounded-md text-sm"
                type="button"
                phx-value-id={@id}
                phx-click="mark-done"
              >
                Mark as Done
              </button>
            <% end %>
```

We have deliberately made these two separate events but you can very well use one event to handle both cases (we will leave this up to you to figure it out!). Finally, we can implement the behavior of the buttons via these events:

```elixir
  def handle_event(
        "mark-not-done",
        %{"id" => task_id},
        %Phoenix.LiveView.Socket{assigns: %{todo_list: todo_list}} = socket
      ) do
    updated_todo_list =
      todo_list
      |> Enum.with_index()
      |> Enum.map(fn {todo, id} ->
        if String.to_integer(task_id) == id do
          Map.update(todo, :is_done?, false, fn _ -> false end)
        else
          todo
        end
      end)

    {:noreply, assign(socket, todo_list: updated_todo_list)}
  end

  def handle_event(
        "mark-done",
        %{"id" => task_id},
        %Phoenix.LiveView.Socket{assigns: %{todo_list: todo_list}} = socket
      ) do
    updated_todo_list =
      todo_list
      |> Enum.with_index()
      |> Enum.map(fn {todo, id} ->
        if String.to_integer(task_id) == id do
          Map.update(todo, :is_done?, true, fn _ -> true end)
        else
          todo
        end
      end)

    {:noreply, assign(socket, todo_list: updated_todo_list)}
  end
```

Here, we are using `Enum.with_index/1` and `Enum.map/2` to implement the update logic. Since state is immutable in Elixir, we have to re-create the to-do list with the `is_done?` field of the target to-do item changed to the appropriate value.

Now, if you ran the application, you will be able to add new to-do items and change their completion status:

<figure><img src="/files/2f4p9GAYv0FgKjPH5Pn5" alt=""><figcaption></figcaption></figure>

## What's next?

:tada: Woohoo! You now have a to-do list application! Feel free to try implementing the other features like deleting and editing to-dos.

{% hint style="info" %}
For editing, you can try making the title editable. For deletion, use a similar logic as updating the "done" status of the to-do items.
{% endhint %}

However, if you refresh the page, all of your to-dos will disappear! That's not good... This is where data persistence comes in. As mentioned in the introduction chapter, the next chapter is entirely optional but highly recommended for you to gain a holistic understanding of using Phoenix for web development (since data persistence is basically a must-have in production applications!)


# Data persistence

{% hint style="info" %}
Depending on time, this chapter may not have been covered during the recording/live talk. Fret not, we have documented the process of setting up data persistence with Phoenix in this chapter.
{% endhint %}

If you want to checkout data persistence in action, go to the `complete` branch by doing `git switch complete`.

The core essentials for data persistence with SQLite3 should have already been setup for you when you first initialized the project. Phoenix uses the widely popular Ecto database library that helps abstract the process of database connection and querying on your behalf.

## Creating a schema and migration

Phoenix comes with various handy [Mix](/elixir-fundamentals/mix) tasks to help with this task. Use the following command to generate the necessary database schema and migration file for the to-do table:

```bash
mix phx.gen.schema Todo todo \
  title:string \
  description:string \
  is_done:boolean \
  --binary-id
```

The command above will automatically generate a file under `lib/practical_elixir_demo/` called `todo.ex` along with a database migration file under `priv/repo/migrations`.

If you open `todo.ex`, you will see the following:

{% code title="lib/practical\_elixir\_demo/todo.ex" %}

```elixir
defmodule PracticalElixirDemo.Todo do
  alias PracticalElixirDemo.Repo
  use Ecto.Schema
  import Ecto.Query
  import Ecto.Changeset

  @primary_key {:id, :binary_id, autogenerate: true}
  @foreign_key_type :binary_id
  schema "todo" do
    field :description, :string
    field :title, :string
    field :is_done, :boolean, default: false

    timestamps(type: :utc_datetime)
  end

  @doc false
  def changeset(todo, attrs) do
    todo
    |> cast(attrs, [:title, :description, :is_done])
    |> validate_required([:title, :description, :is_done])
  end
end
```

{% endcode %}

To quickly run through what you've done so far:

1. `mix phx.gen.schema`: creates a database schema and corresponding migration file
   * Contains fields `title`, `description`, and `is_done`
   * Has auto-generated UUID from `--binary-id`
   * Table name is `todo`
   * Elixir module name is `Todo`
2. The schema module contains:
   * `schema` declaration for the same fields we declared during `mix phx.gen.schema`
   * `changeset/2` function that provides model validation

## Running database migrations

Before running the migration for the new `todo` table, let us first delete the existing `Todo` and `TodoItem` modules (by deleting the files). These are all redundant modules. You may notice some compile errors as there were some references to the `get_items/0` function in `Todo`. You may add a dummy function to the new `Todo` module we have made:

{% code title="lib/practical\_elixir\_demo/todo.ex" %}

```elixir
defmodule PracticalElixirDemo.Todo do
  # ...
  
  def get_items() do
    []
  end
  
  # ...
end
```

{% endcode %}

Finally, move the `todo.ex` file created into the `todo/` folder.

Now, you can run the migration via:

```
mix ecto.migrate
```

Then, make sure you restart the local development server.

You may also need to make the following changes to the HEEx and functional component to work with the latest changes:

{% code title="lib/practical\_elixir\_demo\_web/live/todo\_live.ex" %}

```html
    <div class="flex gap-x-4 mb-4 last:mb-0 items-center">
      <%= if @item.is_done do %>
        <p>✅</p>
      <% else %>
        <p>❌</p>
      <% end %>
      <div class="w-full">
        <div class="flex justify-between items-center w-full">
          <p>
            <%= @item.title %>
          </p>

          <div>
            <%= if @item.is_done do %>
              <button
                class="bg-blue-300 px-3 py-1 font-bold rounded-md text-sm"
                type="button"
                phx-value-id={@id}
                phx-click="mark-todo"
              >
                Mark as Not Done
              </button>
            <% else %>
              <button
                class="bg-green-300 px-3 py-1 font-bold rounded-md text-sm"
                type="button"
                phx-value-id={@id}
                phx-click="mark-todo"
              >
                Mark as Done
              </button>
            <% end %>
            <button class="bg-yellow-300 px-3 py-1 font-bold rounded-md text-sm">Edit</button>
            <button class="bg-red-300 px-3 py-1 font-bold rounded-md text-sm">Delete</button>
          </div>
        </div>
        <p class="italic">
          <%= @item.description %>
        </p>
      </div>
    </div>
```

{% endcode %}

Note the use of `is_done` instead of `is_done?`

{% code title="lib/practical\_elixir\_demo\_web/live/todo\_live.html.heex" %}

```html
<div class="w-[40%] mx-auto my-8">
  <h1 class="font-bold text-3xl my-8 py-4 px-4 bg-slate-100">Todo List</h1>
  <form phx-submit="add-todo" class="flex justify-between items-center mb-8">
    <input type="text" placeholder="New task" name="task-name" class="rounded-md w-full" />
    <button type="submit" class="px-4 py-2 bg-green-100 rounded-md ml-2">
      Add
    </button>
  </form>
  <%= for item <- @todo_list do %>
    <.todo_item item={item} id={item.id} />
  <% end %>
</div>
```

{% endcode %}

Since `todo_list` should now contain the database schema objects that already have a built-in `id` field (random UUID), we can use that as the `id` instead of the relative position.

If you refresh your page, you should see that there are no to-do items in the to-do list. This is perfectly normal as your database table is still empty.

## Retrieving all to-do items

Let's first populate the behavior of the dummy `get_items/0` function we made earlier. We can use the `PracticalElixirDemo.Repo` module to provide some helper functions to easily do that:

{% code title="lib/practical\_elixir\_demo/todo/todo.ex" %}

```elixir
defmodule PracticalElixirDemo.Todo do
  alias PracticalElixirDemo.Repo

  # ...

  def get_items() do
    Repo.all(__MODULE__)
  end
  
  # ...
end

```

{% endcode %}

`Repo.all(__MODULE__)` is the same as `Repo.all(PracticalElixirDemo.Todo)` and all it does is perform a `SELECT * FROM todo;` for us and map the results into `PracticalElixirDemo.Todo` structs that we can use in the front-end.

## Creating to-do items

Then, to create a new to-do item, we can continue using the helper functions from `Repo` and use the `Repo.insert/2` function:

{% code title="lib/practical\_elixir\_demo/todo/todo.ex" %}

```elixir
defmodule PracticalElixirDemo.Todo do
  alias PracticalElixirDemo.Repo

  # ...

  def create_todo(title, description \\ nil) do
    Repo.insert(%__MODULE__{
      title: title,
      description: description,
      is_done: false
    })
  end
  
  # ...
end

```

{% endcode %}

Similar to `Repo.all/1`, `Repo.insert/2` performs an `INSERT INTO todo VALUES (...)` query on your behalf.

We can replace the `add-todo` event in our LiveView controller with a call to this `create_todo/2` function:

{% code title="lib/practical\_elixir\_demo\_web/live/todo\_live.ex" %}

```elixir
  def handle_event("add-todo", %{"task-name" => task_name}, socket) do
    Todo.create_todo(task_name)
    {:noreply, assign(socket, todo_list: Todo.get_items())}
  end
```

{% endcode %}

## Marking items as done/not done

The final piece of behavior we are migrating is the mark as done/not done functionality. In this case, we will collapse the behavior into a single `mark-todo` event that both the buttons will use (be sure to update the functional component's `phx-click` binding):

{% code title="lib/practical\_elixir\_demo\_web/live/todo\_live.ex" %}

```elixir
  def handle_event("mark-todo", %{"id" => task_id}, socket) do
    Todo.mark_todo(task_id)
    {:noreply, assign(socket, todo_list: Todo.get_items())}
  end
```

{% endcode %}

This way, we leave the toggling behavior to the `Todo` model:

```elixir
defmodule PracticalElixirDemo.Todo do
  alias PracticalElixirDemo.Repo

  # ...

  def mark_todo(id) do
    todo = Repo.one!(from t in __MODULE__, where: t.id == ^id)
    updated_todo = Ecto.Changeset.change(todo, is_done: !todo.is_done)
    Repo.update!(updated_todo)
  end
  
  # ...
end

```

We first retrieve the matching to-do based on the given `id`, and then, we toggle the `is_done` field using `Ecto.Changeset.change/2` function and then perform the update using `Repo.update!/1`.

{% hint style="info" %}
The `!` that follow `one` and `update` are to indicate functions that raise an exception when an entry is not found or cannot be updated respectively. They have counterparts that return an `:error` state instead, but we have opted to avoid using them this time.
{% endhint %}

## Voilà :tada:

If you restart your application now, you can play around with the to-do list and you will notice that the to-do items are persisted even after refreshing the page.

This also concludes this guide on practical functional programming with Elixir and Phoenix! As mentioned earlier, the complete code for the application built for this guide is found on the `complete` branch of [this repository.](https://github.com/woojiahao/practical_elixir_demo) If you are interested in learning more about web development with Phoenix, please refer to the [Resources](/resources) for the recommended readings/resources to follow!

All the best in your Elixir journey! :D


# Resources

For more resources about Elixir and Phoenix, please refer to the following list:

* [Official Elixir guide](https://hexdocs.pm/elixir/introduction.html)
* [Official base Phoenix guide](https://hexdocs.pm/phoenix/up_and_running.html)
* [Official Phoenix LiveView guide](https://hexdocs.pm/phoenix_live_view/welcome.html)
* [Phoenix in Action](https://www.manning.com/books/phoenix-in-action) (Book)
* [Programming Elixir 1.6](https://pragprog.com/titles/elixir16/programming-elixir-1-6/) (Book)
* [Ecto documentation](https://hexdocs.pm/ecto/3.10.1/Ecto.html)


