Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

The Elixir Beginner

elixirbeginner.livemd

The Elixir Beginner

Mix.install([
  {:kino, "~> 0.16.0"}
])

🧱 1. All it’s about functions!

Let’s implement a simple module like this:

graph TB
    subgraph Bike
        create_bike/1
    end

elixir implementation

defmodule Bike do
  
  def create_bike(id) do
    %{id: id, name: "bike-#{id}"}
  end
  
end
defmodule Bike do
  
  @doc """
    This function creates a 
     new bike map by a given ID

     iex> Bike.create_bike(1)

    Returns
      %{id: 1, name: "bike-1"} 
  """
  @spec create_bike(integer()) 
    :: %{id: integer(), name: String.t()}
  def create_bike(id) do
    %{id: id, name: "bike-#{id}"}
  end
  
end

# Bikes.create_bike(1) 

Let’s create a new module to create many bikes by a given number.

graph TB
    subgraph Bikes
        create_bike/1
    end

    subgraph Station
        create_bikes/1
    end

In Elixir you'll find code execution inside functions that belong to a Module.

defmodule Station do
  @moduledoc """
    This module is for create all the bikes.
  """

  @doc """
    iex> Station.create_bikes(10)
  """
  def create_bikes(number) do
    Enum.map(1..number, 
      fn index -> 
        Bike.create_bike(index) # Use the Bike module
      end)
  end
  
end

10 |> Station.create_bikes() |> Kino.DataTable.new()

⭐️ 2. Immutability

In languages like JavaScript is common find this:

let bike = { id: "bike-1" };
bike.station = "station 1"; // Direct mutation
// obj -> {  id: "bike-1", station: "station 1" }

Immutability is one of the core principles of the Erlang VM and the Erlang ecosystem

An immutable data structure cannot be changed after it’s created.


#### 1. You have a bike
bike = %{id: "bike-1"}

#### 2. Add a new key to the bike map
Map.put(bike, :station, "station-1")

#### 3. bike is not going to change
bike # %{id: "bike-1"}

#### Step 2 is going to create a new map
%{id: "bike-1", station: "station-1"}
bike = %{id: "bike-1"}

bike_with_station = Map.put(bike, :station, "station-1")

bike_with_tag = Map.put(bike_with_station, :tag, "station-tag-1")

bike_with_availability = Map.put(bike_with_tag, :availability, 100)

🍰 3. Data Transformation

Since you can't modify data once it's created, then you need to transform it.

defmodule MyApp.Bike do
  def create_bike(id) do
    %{id: id, name: "bike-#{id}"}
  end

  def add_status(bike, status) do
    Map.put(bike, :status, status)
  end

  def add_tag(%{id: id, status: status} = bike) do
    Map.put(bike, :tag, "##{id}-#{status}")
  end
end
## Data transformation
id = "1"
new_bike = MyApp.Bike.create_bike(id)
bike_with_status = MyApp.Bike.add_status(new_bike, "available")
bike_with_status_and_tag = MyApp.Bike.add_tag(bike_with_status)
## Data transformation using pipe operator
"1"
|> MyApp.Bike.create_bike() #step 1
|> MyApp.Bike.add_status("available") #step 2
|> MyApp.Bike.add_tag() #step 3
defmodule MyApp2.Bikes do
  @moduledoc """
  The Bikes context — handles business logic and persistence.
  """
  alias MyApp.Repo
  alias MyApp.Bikes.Bike

  # CREATE
  def create_bike(attrs \\ %{}) do
    %Bike{}
    |> Bike.changeset(attrs)
    |> Repo.insert()
  end

  # READ
  def list_bikes do
    Repo.all(Bike)
  end

  def get_bike!(id), do: Repo.get!(Bike, id)

  # UPDATE
  def update_bike(%Bike{} = bike, attrs) do
    bike
    |> Bike.changeset(attrs)
    |> Repo.update()
  end

  # DELETE
  def delete_bike(%Bike{} = bike) do
    Repo.delete(bike)
  end
end

⚽️ 4. Concurrency Oriented Programming


live_book_process = self()

## Raw Process 
spawn(fn ->
  bikes = Station.create_bikes(10)
  send(live_book_process, bikes)
end)

receive do
  msg -> IO.inspect msg
end
sequenceDiagram
    autonumber
    LiveBook->>Process: create process
    Process->>Process: create bikes
    Process-->>LiveBook: bikes created

Enum.each(1..10, fn idx -> 
  pid = spawn(fn ->
    receive do
      {:create_bike, id} -> IO.inspect Bike.create_bike(id)
    end
  end)

  Process.send(pid, {:create_bike, idx}, [])
end)