# Learning Elixir in Action

I don't know anything about Elixir language, but when I want to learn a programming language, I try to make the syntax more understandable for myself with examples... **I'm not even sure at this point if the title of this post should be "Learning Elixir in Action" or "Learning OCaml in Action"!?**

### Examples

Here we want to implement about 6 programs with different levels of difficulty — there are definitely many ways to implement them, but we will only write some of them (both due to lack of knowledge, and maybe to prevent the post from getting too long)!

#### Fizzbuzz

Choose two numbers and two labels. Now, if a number is divisible by the first number you chose, assign the first label to it; if a number is divisible by the second number you chose, assign the second label to it; and if a number is divisible by both of them, merge the labels together and assign it to that number...

```elixir
defmodule Fizzbuzz do # Or `FizzBuzz`/`Main`/`M` and anything else
  defp classify(n) when rem(n, 15) == 0, do: "Fizzbuzz"
  defp classify(n) when rem(n, 3) == 0, do: "Fizz"
  defp classify(n) when rem(n, 5) == 0, do: "Buzz"
  defp classify(n), do: n

  def fizzbuzz(a, b) do
    for n <- a..b, do: classify(n)
    |> IO.puts()
  end
end

Fizzbuzz.fizzbuzz(1, 20)
```

`defp` means that the function cannot be invoked outside the module. In the `fizzbuzz` function, we defined a loop for the given range, and then we piped the current labeled number to `IO.puts` to see the result in the console.

Version 2 of this ridiculous game:

```elixir
defmodule Fizzbuzz do
  defp classify(n) when rem(n, 15) == 0, do: "Fizzbuzz"
  defp classify(n) when rem(n, 3) == 0, do: "Fizz"
  defp classify(n) when rem(n, 5) == 0, do: "Buzz"
  defp classify(n), do: n

  def fizzbuzz(a, b) do
    a..b
    |> Enum.map(&classify/1) # A reference to the classifier function that takes an argument
    |> Enum.each(&IO.puts/1)
  end
end

Fizzbuzz.fizzbuzz(1, 20)
```

As you can see in the above implementations, we can write comments in the code like most programming languages; but I will put the things that need more explanation in the main body of this post...

Implementations in Bash and C++ are available in my repository:  
[github.com/sheikhartin/fizzbuzz](http://github.com/sheikhartin/fizzbuzz)

#### Linear Search Algorithm

In search algorithms in general, we should return a Boolean to say whether the target element exists in the given list or not; and it's not bad to throw its index too...

```elixir
defmodule LinearSearch do
  def search(list, target) do
    Enum.map(list, fn val ->
      val == target
    end)
    |> Enum.any?()
    |> IO.puts()
  end
end

LinearSearch.search([1, 2, 3, 4, 5], 4)
```

Version 2:

```elixir
defmodule LinearSearch do
  def search(list, target) do
    list
    |> Enum.with_index()
    |> Enum.find(fn val -> elem(val, 0) == target end)
    |> case do
      nil -> IO.puts "Not found!"
      {_, idx} -> IO.puts "Found at index: #{idx}"
    end
  end
end

LinearSearch.search([1, 2, 3, 4, 5], 4)
```

#### Nonsense Authentication

Nothing, we just need to check the entered password in our database to find out whether the user exists or not. *It is nonsense because by looking at the code you can see the username(s) and password(s)...*

```elixir
defmodule NonsenseAuthentication do
  @username "sheikhartin"
  @password "358"

  def auth(username, password), do: username == @username && password == @password
end

NonsenseAuthentication.auth(
  String.trim(IO.gets "Username: "),
  String.trim(IO.gets "Password: ")
)
|> case do
  false -> "You are not allowed to be here!"
  true -> "Welcome back boss!"
end
|> IO.puts()
```

In version 2, we use a struct and another module to improve design and readability...

```elixir
defmodule Hash do
  def sha256(text), do: :crypto.hash(:sha256, text) |> Base.encode64()
end

defmodule User do
  defstruct username: nil, hashed_password: nil
end

defmodule NonsenseAuthentication do
  @users [
    %User{username: "root", hashed_password: Hash.sha256("toor")},
    %User{username: "sheikhartin", hashed_password: Hash.sha256("helloworld")},
  ]

  def auth(username, password) do
    hashed_password = Hash.sha256(password)

    @users
    |> Enum.find(fn user -> user.username == username end)
    |> case do
      nil -> {:error, "User not found!"}
      %User{hashed_password: ^hashed_password} -> {:ok, "Welcome back `#{username}`!"}
      _ -> {:error, "Incorrect password!"}
    end
  end
end

case NonsenseAuthentication.auth(
  String.trim(IO.gets "Username: "),
  String.trim(IO.gets "Password: ")
) do
  {:ok, msg} -> IO.puts msg
  {:error, reason} -> IO.puts reason
end
```

#### Calculator

The calculators that are implemented here should work based on user input to know more about [IO](https://en.wikipedia.org/wiki/Input/output)...

```elixir
defmodule BubbleSort do
  defp bubble([head | rest]), do: bubble(rest, head, [])
  defp bubble([], acc, sorted), do: Enum.reverse([acc | sorted])
  defp bubble([head | rest], acc, sorted) when acc <= head, do:
    bubble(rest, head, [acc | sorted])
  defp bubble([head | rest], acc, sorted), do:
    bubble(rest, acc, [head | sorted])

  defp sort(list, 0), do: list
  defp sort(list, n), do: list |> bubble |> sort(n - 1)
  def sort(list) when is_list(list), do: sort(list, length(list))
end

[5, 8, 9, 11, 14, 15] = BubbleSort.sort([11, 15, 8, 5, 9, 14])
```

With `@spec`, you can define the type of parameters in a function and its return value.

Version 2 is more sensitive to errors:

```elixir
defmodule Calculator do
  @spec add(float, float) :: {:ok, float}
  def add(a, b), do: {:ok, a + b}

  @spec subtract(float, float) :: {:ok, float}
  def subtract(a, b), do: {:ok, a - b}

  @spec multiply(float, float) :: {:ok, float}
  def multiply(a, b), do: {:ok, a * b}

  @spec divide(float, float) :: {:ok, float} | {:error, String.t()}
  def divide(a, b) when b != 0, do: {:ok, a / b}
  def divide(_, _), do: {:error, "Cannot divide by zero!"} # Or `divide(_a, 0.0)`

  @spec power(float, float) :: {:ok, float}
  def power(a, b), do: {:ok, :math.pow(a, b)}
end

case {
  IO.gets("Enter the first number: ")
  |> String.trim()
  |> Float.parse(),
  IO.gets("And the second number: ")
  |> String.trim()
  |> Float.parse(),
} do
  {{number1, ""}, {number2, ""}} ->
    for {operation, result} <- [
      {:addition, Calculator.add(number1, number2)},
      {:subtraction, Calculator.subtract(number1, number2)},
      {:multiplication, Calculator.multiply(number1, number2)},
      {:division, Calculator.divide(number1, number2)},
      {:power, Calculator.power(number1, number2)},
    ] do
      case result do
        {:ok, value} ->
          IO.puts "#{Atom.to_string(operation) |> String.capitalize()}: #{value}"
        {:error, reason} ->
          IO.puts "#{Atom.to_string(operation) |> String.capitalize()}: #{reason}"
      end
    end
  _ -> IO.puts "An error has occurred; make sure you enter valid numbers..."
end
```

I don't have a specific style guide yet, and I also don't know if the above solution is standard or not!?

#### Bubble Sort Algorithm

[*"Bubble sort, sometimes referred to as sinking sort, is a simple sorting algorithm that repeatedly steps through the input list element by element, comparing the current element with the one after it, swapping their values if needed."*](https://en.wikipedia.org/wiki/Bubble_sort#)

```elixir
defmodule BubbleSort do
  defp bubble([head | rest]), do: bubble(rest, head, [])
  defp bubble([], acc, sorted), do: Enum.reverse([acc | sorted])
  defp bubble([head | rest], acc, sorted) when acc <= head, do:
    bubble(rest, head, [acc | sorted])
  defp bubble([head | rest], acc, sorted), do:
    bubble(rest, acc, [head | sorted])

  defp sort(list, 0), do: list
  defp sort(list, n), do: list |> bubble |> sort(n - 1)
  def sort(list) when is_list(list), do: sort(list, length(list))
end

[5, 8, 9, 11, 14, 15] = BubbleSort.sort([11, 15, 8, 5, 9, 14])
```

Maybe it is better to use `curr` instead of `acc`, and `tail` instead of `rest` as the names of our variables.

For the first time, I want to analyze the code function by function!

* **bubble/1**: This function initializes the bubble sort process for a [single pass](https://stackoverflow.com/a/13114130/13414133). It calls `bubble/3` with the rest of the list, the head as the current element to be compared, and an empty list as the accumulator for sorted elements.
    
* **bubble/3**: Reverses the accumulator list and prepends the current element to it. In fact, it puts the largest element at the end of the sorted list.
    
* **bubble/3 when acc &lt;= head**: It calls itself recursively with the rest of the list, the head as the new current element, and the accumulator with the current element added to it.
    
* **bubble/3**: This function continues the bubble sort pass when the current element is greater than the next element.
    
* **sort/2**: This function returns the list as is, indicating that the sorting is complete.
    
* **sort/2**: Recursively calls itself, decrementing `n` by 1.
    
* **sort/1**: Calls the `sort/2` function with the list and its length, which determines how many passes are needed to sort the list.
    

#### Rock, Paper, Scissors

You know it better than me!

```elixir
defmodule RockPaperScissors do
  @moves [
    :rock,
    :paper,
    :scissors,
  ]

  def play() do
    for _ <- 0..5 do
      computer_move = Enum.random(@moves)
      user_move = case IO.gets("Choose your move [1/Rock 🪨, 2/Paper 𐇳, 3/Scissors ✂]: ")
        |> String.trim()
        |> Integer.parse() do
          {move, ""} when move in 1..3 -> Enum.at(@moves, move-1)
        _ -> :invalid
      end

      case {computer_move, user_move} do
        {move, move} ->
          IO.puts "It's a tie! Both chose #{move}."
        {:rock, :scissors} ->
          IO.puts "Computer wins! Rock 🪨 beats scissors ✂."
        {:scissors, :paper} ->
          IO.puts "Computer wins! Scissors ✂ beats paper 𐇳."
        {:paper, :rock} ->
          IO.puts "Computer wins! Paper 𐇳 beats rock 🪨."
        {:scissors, :rock} ->
          IO.puts "You win! Rock 🪨 beats scissors ✂."
        {:paper, :scissors} ->
          IO.puts "You win! Scissors ✂ beats paper 𐇳."
        {:rock, :paper} ->
          IO.puts "You win! Paper 𐇳 beats rock 🪨."
        {_, :invalid} ->
          IO.puts "Invalid move. Please enter 1, 2, or 3."
      end
    end
  end
end

RockPaperScissors.play # Or `RockPaperScissors.play()`
```

In the above implementation, I tried to break my code into smaller parts... This is very good, but I am not a fan of using variables too much!

Version 2 for easier recognition of win conditions:

```elixir
defmodule RockPaperScissors do
  @moves [
    :rock,
    :paper,
    :scissors,
  ]
  @win_conditions %{
    rock: :scissors,
    paper: :rock,
    scissors: :paper,
  }

  def play(n \\ 5) when n > 0 do
    for _ <- 0..n-1 do
      computer_move = Enum.random(@moves)
      user_move = case IO.gets("Choose your move [1/Rock, 2/Paper, 3/Scissors]: ")
        |> String.trim()
        |> Integer.parse() do
          {move, ""} when move in 1..3 -> Enum.at(@moves, move-1)
        _ -> :invalid
      end

      cond do
        user_move == :invalid ->
          IO.puts "Invalid move. Please enter 1, 2, or 3."
        computer_move == user_move ->
          IO.puts "It's a tie! Both chose #{computer_move}."
        @win_conditions[computer_move] == user_move ->
          IO.puts "Computer wins! #{computer_move |> Atom.to_string() |> String.capitalize()} beats #{user_move}."
        @win_conditions[user_move] == computer_move ->
          IO.puts "You win! #{user_move |> Atom.to_string() |> String.capitalize()} beats #{computer_move}."
      end
    end
  end
end

RockPaperScissors.play
```

The default value considered as the number of times to play the game is 5. And yeah, it doesn't look very functional!

Implementation in Bash:  
[github.com/sheikhartin/rock-paper-scissors](http://github.com/sheikhartin/rock-paper-scissors)

### Last Words

**In my opinion, to learn a language (of any kind), you have to work many hours so that you don't think about the syntax anymore...** I'm still not sure if I should continue learning this language or not; but after a few years of procedural programming, *it makes me think differently about how to code, which is lovely and interesting!* Maybe I should write a more important project with Elixir and then decide what to do...

Other learning resources:

* [Elixir School](https://elixirschool.com/en)
    
* [Tutorialspoint](https://www.tutorialspoint.com/elixir/index.htm)
