Developer Blog

Tipps und Tricks für Entwickler und IT-Interessierte

Elixir | Einstieg in Elixir

Einführung

Elixir ist eine dynamische, funktionale Sprache zum Erstellen skalierbarer und wartbarer Anwendungen. Elixir nutzt die Erlang VM, die dafür bekannt ist, verteilte und fehlertolerante Systeme mit geringer Latenz auszuführen.

Außerdem wird Elixir wird erfolgreich in Webentwicklung, eingebetteter Software, Datenaufnahme und Multimediaverarbeitung in einer Vielzahl von Branchen eingesetzt.

Hier ein erster Blick:

iex> "Elixir" |> String.graphemes() |> Enum.frequencies()



Oder, wie es Wikipedia beschreibt:

Elixir ist eine funktionaleElixir Grundlagen

Hier folge eine kleine Einführung in die Programmiersprache Elixir

Grundlegende Datentypen

Elixir supports the basic data types that other programming languages have. You can use integers (whole numbers), floats (decimal numbers), booleans (true or false) and strings (which are wrapped in double quotes, are UTF-8 encoded, and support line breaks).

Probieren wir in der Elixit Shell einfach die nachfolgenden Beispiele aus.

Starten Sie dazu die Elixit Shell:

iex

Oder wenn Sie mit Powershell arbeiten

iex.bat
iex(1)> 5
5

iex(2)> 4.32
4.32

iex(3)> true
true

iex(4)> "Hello World!"
"Hello World!"

iex(5)> "Hello
...(5)> World!"
"Hello\nWorld!"

Beachten Sie, dass wir im letzten Beispiel Umschalt + Eingabetaste verwenden, um einen Befehl auszuführen, der sich über mehrere Zeilen erstreckt.

Sie können auch Variablen und einfache Operatoren verwenden, um Werte zu vergleichen oder Zeichenfolgen zu verketten:

iex(1)> my_number = 6
6

iex(2)> my_number
6

iex(3)> my_number >= 7
false

iex(4)> my_number == 6
true

iex(5)> my_name = "Tristan"
"Tristan"

iex(6)> "Hello " <> my_name
"Hello Tristan"

iex(7)> "Hello #{my_name}"
"Hello Tristan"

Atoms

Atome sind eine besondere Art von Datentyp, die Konstanten (unveränderlichen Variablen) ähneln, ihr einziger Wert jedoch der ihnen gegebene Name ist.

iex(1)> :my_atom
:my_atom

iex(2)> :my_atom == "my_atom"
false

iex(3)> :my_atom == true
false

iex(4)> :my_atom == :my_atom
true

iex(5)> :my_other_atom = 4
** (MatchError) no match of right hand side value: 4

Im letzten Beispiel sehen wir, dass wir einem Atom keinen bestimmten Wert zuweisen können. Auch hier ist ihr einziger Wert ihr Name!

Lists

Listen in Elixir können jeden Datentyp enthalten und beliebig lang sein. Sie können mit ++ zwei Listen verketten oder mit — eine Liste von einer anderen subtrahieren.

iex(1)> [5, "Elixir", :ok]
[5, "Elixir", :ok]
iex(2)> [1, 2] ++ [3]
[1, 2, 3]
iex(3)> [1, 2, 3] -- [2]
[1, 3]

Beachten Sie, dass Elixir eine unveränderliche Sprache (immutable language) ist.

Wenn Sie diese Vorgänge ausführen, wird die ursprüngliche Liste also nie geändert. Stattdessen wird eine neue Liste zurückgegeben, die Sie in einer Variablen speichern können:

iex(1)> my_list = [1, 2, 3]
[1, 2, 3]

iex(2)> my_list ++ [4]
[1, 2, 3, 4]

iex(3)> my_list
[1, 2, 3]

Listen in Elixir können auch in zwei Teile aufgeteilt werden: den Kopf (head, der mit der hd-Funktion extrahiert werden kann) und den Schwanz (tail , der mit der tl-Funktion extrahiert werden kann). Der Kopf ist das erste Element der Liste und der Schwanz ist der Rest:

iex(1)> hd [5, "Banana", :ok]
5

iex(2)> tl [5, "Banana", :ok]
["Banana", :ok]

Tuples

Tupel ähneln Listen, aber sie speichern ihre Elemente zusammenhängend im Speicher. Dadurch wird der Zugriff auf Tupelelemente schneller als auf Listenelemente, jedoch ist deren Änderung langsamer.

Sie können ein Tupel als eine Ansammlung von Werten sehen, um eine Art Ressource zu bilden, während Listen verwendet werden, um Dinge aufzuzählen.

iex(7)> {"Tristan", 24}
{"Tristan", 24}

Sie werden feststellen, dass viele Elixir-Funktionen Tupel zurückgeben, um zwischen Erfolgen und Misserfolgen zu unterscheiden, z. B.: {:ok, value} oder {:error, error_message}

Maps

Karten (Maps) sind der Schlüsselwertspeicher (key-value store) von Elixir. Die Schlüssel können einen beliebigen Typ haben und zeigen mit => auf einen Wert.

iex(1)> company_phones =

iex(2)> company_phones.apple
"iphone"

Most of the time, you’ll probably want the keys in your map to be atoms, like in the example above. When that’s the case, there’s a nice shorthand syntax where we can use a colon instead of “=>” to point to the values, and omit the :-character at the beginning of our atoms:

iex(3)> company_phones =



Structs



Eine Struktur (struct) ist einer Map sehr ähnlich, bietet jedoch einige zusätzliche Funktionen – Sie können die Anzahl der Schlüssel begrenzen und ihnen Standardwerte zuweisen.

Um Strukturen (structs) auszuprobieren, müssen wir eine Elixir-Datei erstellen, also fahren Sie fort und erstellen Sie eine neue Datei namens user.ex. Darin werden wir ein neues Modul definieren, in dem wir auch unsere Struktur definieren:

defmodule User do
  defstruct name: "Tristan", age: 24
end

Gehen Sie nun zurück zur IEx-Shell und führen Sie den Befehl c(“user.ex”) aus.

Sie können jetzt eine neue Benutzerstruktur erstellen, indem Sie

❯ iex.bat -S mix
Compiling 1 file (.ex)
Interactive Elixir (1.12.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> c("user.ex")
[User]
iex(2)>
iex(3)>

Conditionals

Die grundlegenden if- und else-Anweisungen funktionieren genau so, wie Sie es erwarten. Beachten Sie, dass wir end verwenden müssen, um anzugeben, wo die if-Anweisungen enden.

iex(4)> if 5 > 4 do
...(4)>   "It's higher!"
...(4)> else
...(4)>   "It's lower"
...(4)> end
"It's higher!"

Sie können sogar die Umkehrung unless verwenden, um Ihren Code lesbarer zu machen:

iex(6)> unless 5 > 6 do
...(6)>   "The laws of mathematics still work!"
...(6)> end
"The laws of mathematics still work!"

Was ist mit “else if”?

In Elixir gibt es kein “else if”. Wenn Sie einen Flow mit mehr als nur zwei Ergebnissen haben, sollten Sie stattdessen wahrscheinlich cond verwenden:

iex(5)> cond do
...(5)>   5 > 6 ->
...(5)>     "This isn't true"
...(5)>   5 == 8 ->
...(5)>     "Neither is this"
...(5)>   true ->
...(5)>     "But this is!"
...(5)> end
"But this is!"

Wenn in Ihrer cond-Anweisung nichts als wahr ausgewertet wird, wird eine Ausnahme ausgelöst! Daher ist es normalerweise eine gute Praxis, immer eine letzte true-Bedingung ganz am Ende Ihrer Anweisung hinzuzufügen (wie wir es in unserem obigen Beispiel getan haben), um eventuelle Ausnahmen zu behandeln.

Schließlich gibt es noch eine case-Anweisung, die den Kontrollfluss in Elixir behandelt, aber dazu werden wir im nächsten Kapitel mehr erfahren …

Erstellen einer App

Im ersten Schritt erstellen wir die grundlegende Elixir Anwendung mit Hilfe des Komandos mix.

❯ mix new starterapp
* creating README.md
* creating .formatter.exs
* creating .gitignore
* creating mix.exs
* creating lib
* creating lib/starterapp.ex
* creating test
* creating test/test_helper.exs
* creating test/starterapp_test.exs

Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:

    cd starterapp
    mix test

Run "mix help" for more commands.

Durch dieses Kommando wird der Ordner starterapp mit der folgenbden Verzeichnisstruktur erstellt

Ersten Test starten

❯ cd starterapp
❯ mix test
Compiling 1 file (.ex)
Generated starterapp app
..

Finished in 0.07 seconds (0.00s async, 0.07s sync)
1 doctest, 1 test, 0 failures

Randomized with seed 697266

Ersten Programmcode schreiben

Funktion say_hello

defmodule StarterApp do

  def hello do
    :world
  end

  def say_hello do
    IO.puts("Hello World")
  end
  
end

Interactive Shell mit unserer App starten

iex -S mix

Unter Poweshell gibt es bereits einen Alias iex. Hier nutzen wir diesen Aufruf

iex.bat -S mix
❯ iex.bat -S mix
Compiling 1 file (.ex)
Generated starterapp app
Interactive Elixir (1.12.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>

Aufruf unserer Funktion

❯ iex.bat -S mix
Compiling 1 file (.ex)
Generated starterapp app
Interactive Elixir (1.12.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> StarterApp.say("Hello", "World")
Hello World!
:ok
iex(2)>

Erweitern der Funktion um Paramter

  def say(greeting, name) do
    IO.puts "#{greeting} #{name}!"
  end
❯ iex.bat -S mix
Compiling 1 file (.ex)
Generated starterapp app
Interactive Elixir (1.12.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> StarterApp.say("Hello", "World")
Hello World!
:ok
iex(2)>

Funktion mit mehrerer, auch optionalen Parametern festlegen

defmodule StarterApp do

  def main() do
    name = IO.gets("Bitte geben Sie Ihrem Name an: ") |> String.trim
    say(name)
  end

  def say("") do
    IO.puts "Sie muessen einen Namen angeben!"
    main()
  end

  def say(name) do
    IO.puts "Hallo #{name}!"
  end

  def say(greeting, name) do
    IO.puts "#{greeting} #{name}!"
  end
end

In der Elixir Shell das Modul neu laden

iex(2)> r StarterApp
warning: redefining module StarterApp (current version defined in memory)
  lib/starterapp.ex:1

{:reloaded, StarterApp, [StarterApp]}
iex(3)>

Aufruf ohne Parameter

iex(3)> StarterApp.say()
** (UndefinedFunctionError) function StarterApp.say/0 is undefined or private. Did you mean one of:

      * say/1
      * say/2

    (starterapp 0.1.0) StarterApp.say()
iex(4)>
iex(4)> StarterApp.say("")
Sie muessen einen Namen angeben!
Bitte geben Sie Ihrem Name an: World
Hallo World!
:ok
iex(5)>

Aufruf mit zwei Parametern

iex(8)>  StarterApp.say("Hallo", "Welt")
Hallo Welt!
:ok
iex(5)>

Elixir| Einstieg in Elixir und Phoenix

Installation

Erlang / Elixir

Unter Windows

Installationspakete unter Erlang / Downloads (Version 24.0) und Elixir / Downloads (Web Installer)

Erstellen einer ersten Anwendung

Anwendung mit dem Namen ‘app’ erstellen.

Als Vorlage wird ‘live’ werwendet. Diese vewendet Phoenix.LiveView und erleichter die Erstellung von Web Anwendungen.

mix phx.new --live app

Frontend erstellen

cd app
cd assets 
npm install 
node node_modules/webpack/bin/webpack.js --mode development

Datenbank-Instanz einrichten und starten

Elixir und Phoenix verwendet in der Standardeinstellung eine PostgreSQL Datenbanken.

Der einfachste Weg, eine lauffähige PostgrSQL Datenbank einzurichten, ist mit Hilfe von Docker.

Erstellen Sie hierzu einen Datei docker-compose.yml:

version: '3.5'

networks:
  postgres:
    name: ${POSTGRES_CONTAINER:-workshop_elixir_postgres}
    driver: bridge

volumes:
    postgres:
      name: postgres

services:
  postgres:
    container_name: ${POSTGRES_CONTAINER:-workshop_elixir_postgres}
    image: postgres
    environment:
      POSTGRES_USER: ${POSTGRES_USER:-postgres}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres}
      PGDATA: /data/postgres

    volumes:
       - postgres:/data/postgres

    ports:
      - "5432:5432"

    networks:
      - postgres

    restart: unless-stopped

Starten Sie die Datenbank in einem eigenen Fenster mit dem nachfolgenden Kommando:

docker compose up
[+] Running 14/14
 - db Pulled
   - b4d181a07f80 Already exists
   - 46ca1d02c28c Pull complete
   - a756866b5565 Pull complete
   - 36c49e539e90 Pull complete
   - 664019fbcaff Pull complete 
   - 727aeee9c480 Pull complete
   - 796589e6b223 Pull complete
   - 6664992e747d Pull complete
   - 0f933aa7ccec Pull complete
   - 99b5e5d88b32 Pull complete
   - a901b82e6004 Pull complete
   - 625fd35fd0f3 Pull complete
   - 9e37bf358a5d Pull complete
[+] Running 1/1
 - Container elixis_postgres  Started
Attaching to elixis_postgres
elixis_postgres  | The files belonging to this database system will be owned by user "postgres".
elixis_postgres  | This user must also own the server process.
...
...
...
elixis_postgres  | 2021-07-12 15:01:08.042 UTC [1] LOG:  database system is ready to accept connections

Datenbanktabellen erstellen

Festlegen der Datenbank-Verbindungsparameter in der Datei config/dev.exs.

Wir verwenden dabie die gleichen Werte, die wir in der Datei docker-compose.yml verwendet haben:

POSTGRES_USERusername
POSTGRES_PASSWORDpassword
POSTGRES_DBdatabase
config :app, App.Repo,
  username: "root",
  password: "root",
  database: "playground",
  hostname: "localhost",
  show_sensitive_data_on_connection_error: true,
  pool_size: 10

Erstellen der Datenbank-Tabellen

mix ecto.create

Webserver starten

mix phx.server

Links

Elixir und das Web

Elixir und Datenbanken

  • Ecto – domain specific language for writing queries and interacting with databases Github

Tips zum Erlernen von Elixir

Screencasts

Übungen /Exercises

Bücher