11. July 2021
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:
<pre class="EnlighterJSRAW" data-enlighter-group="" data-enlighter-highlight="" data-enlighter-language="generic" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-theme="" data-enlighter-title="">iex> "Elixir" |> String.graphemes() |> Enum.frequencies()
%{"E" => 1, "i" => 2, "l" => 1, "r" => 1, "x" => 1}
Oder, wie es Wikipedia beschreibt:
Elixir ist eine funktionale, nebenläufige Allzweck-Programmiersprache, die auf der virtuellen Maschine von Erlang (BEAM) läuft.
Es lassen sich damit verteilte, fehlertolerante, in weicher Echtzeit sowie permanent durchlaufende Programme erstellen.
Darüber hinaus kann Elixir Metaprogrammierung mittels Makros und Polymorphismus mittels Protokollen abbilden.[
Elixir 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:
<pre class="EnlighterJSRAW" data-enlighter-group="" data-enlighter-highlight="" data-enlighter-language="generic" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-theme="" data-enlighter-title="">iex
Oder wenn Sie mit Powershell arbeiten
<pre class="EnlighterJSRAW" data-enlighter-group="" data-enlighter-highlight="" data-enlighter-language="generic" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-theme="" data-enlighter-title="">iex.bat
<pre class="wp-block-preformatted">
<pre class="EnlighterJSRAW" data-enlighter-group="" data-enlighter-highlight="" data-enlighter-language="generic" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-theme="" data-enlighter-title="">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:
<pre class="EnlighterJSRAW" data-enlighter-group="" data-enlighter-highlight="" data-enlighter-language="generic" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-theme="" data-enlighter-title="">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"
<pre class="wp-block-preformatted">
Atoms
Atome sind eine besondere Art von Datentyp, die Konstanten (unveränderlichen Variablen) ähneln, ihr einziger Wert jedoch der ihnen gegebene Name ist.
<pre class="EnlighterJSRAW" data-enlighter-group="" data-enlighter-highlight="" data-enlighter-language="generic" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-theme="" data-enlighter-title="">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.
<pre class="EnlighterJSRAW" data-enlighter-group="" data-enlighter-highlight="" data-enlighter-language="generic" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-theme="" data-enlighter-title="">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:
<pre class="EnlighterJSRAW" data-enlighter-group="" data-enlighter-highlight="" data-enlighter-language="generic" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-theme="" data-enlighter-title="">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:
<pre class="EnlighterJSRAW" data-enlighter-group="" data-enlighter-highlight="" data-enlighter-language="generic" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-theme="" data-enlighter-title="">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.
<pre class="EnlighterJSRAW" data-enlighter-group="" data-enlighter-highlight="" data-enlighter-language="generic" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-theme="" data-enlighter-title="">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.
<pre class="EnlighterJSRAW" data-enlighter-group="" data-enlighter-highlight="" data-enlighter-language="generic" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-theme="" data-enlighter-title="">iex(1)> company_phones = %{:google => "pixel", :apple => "iphone"}
%{apple: "iphone", google: "pixel"}
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:
<pre class="wp-block-preformatted">
<pre class="wp-block-preformatted">iex(3)> company_phones = %{google: "pixel", apple: "iphone"}
<pre class="wp-block-preformatted">%{apple: "iphone", google: "pixel"}
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:
<pre class="EnlighterJSRAW" data-enlighter-group="" data-enlighter-highlight="" data-enlighter-language="generic" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-theme="" data-enlighter-title="">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 %User{} eingeben, und Sie werden sehen, dass sie automatisch alle Standardwerte erhält:
<pre class="EnlighterJSRAW" data-enlighter-group="" data-enlighter-highlight="" data-enlighter-language="generic" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-theme="" data-enlighter-title="">❯ 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)> %User{}
%User{age: 24, name: "Tristan"}
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.
<pre class="EnlighterJSRAW" data-enlighter-group="" data-enlighter-highlight="" data-enlighter-language="generic" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-theme="" data-enlighter-title="">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:
<pre class="EnlighterJSRAW" data-enlighter-group="" data-enlighter-highlight="" data-enlighter-language="generic" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-theme="" data-enlighter-title="">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:
<pre class="EnlighterJSRAW" data-enlighter-group="" data-enlighter-highlight="" data-enlighter-language="generic" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-theme="" data-enlighter-title="">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
.
<pre class="EnlighterJSRAW" data-enlighter-group="" data-enlighter-highlight="1" data-enlighter-language="generic" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-theme="" data-enlighter-title="">❯ 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
<pre class="EnlighterJSRAW" data-enlighter-group="" data-enlighter-highlight="1" data-enlighter-language="generic" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-theme="" data-enlighter-title="">❯ cd starterapp
<pre class="EnlighterJSRAW" data-enlighter-group="" data-enlighter-highlight="1" data-enlighter-language="generic" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-theme="" data-enlighter-title="">❯ 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
<pre class="EnlighterJSRAW" data-enlighter-group="" data-enlighter-highlight="" data-enlighter-language="generic" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-theme="" data-enlighter-title="">defmodule StarterApp do
def hello do
:world
end
def say_hello do
IO.puts("Hello World")
end
end
Interactive Shell mit unserer App starten
<pre class="EnlighterJSRAW" data-enlighter-group="" data-enlighter-highlight="" data-enlighter-language="generic" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-theme="" data-enlighter-title="">iex -S mix
Unter Poweshell gibt es bereits einen Alias iex
. Hier nutzen wir diesen Aufruf
<pre class="EnlighterJSRAW" data-enlighter-group="" data-enlighter-highlight="" data-enlighter-language="generic" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-theme="" data-enlighter-title="">iex.bat -S mix
<pre class="EnlighterJSRAW" data-enlighter-group="" data-enlighter-highlight="" data-enlighter-language="generic" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-theme="" data-enlighter-title="">❯ 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
<pre class="EnlighterJSRAW" data-enlighter-group="" data-enlighter-highlight="" data-enlighter-language="generic" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-theme="" data-enlighter-title="">❯ 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
<pre class="EnlighterJSRAW" data-enlighter-group="" data-enlighter-highlight="" data-enlighter-language="generic" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-theme="" data-enlighter-title=""> def say(greeting, name) do
IO.puts "#{greeting} #{name}!"
end
<pre class="EnlighterJSRAW" data-enlighter-group="" data-enlighter-highlight="" data-enlighter-language="generic" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-theme="" data-enlighter-title="">❯ 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
<pre class="EnlighterJSRAW" data-enlighter-group="" data-enlighter-highlight="" data-enlighter-language="generic" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-theme="" data-enlighter-title="">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
<pre class="EnlighterJSRAW" data-enlighter-group="" data-enlighter-highlight="1" data-enlighter-language="generic" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-theme="" data-enlighter-title="">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
<pre class="EnlighterJSRAW" data-enlighter-group="" data-enlighter-highlight="" data-enlighter-language="generic" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-theme="" data-enlighter-title="">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)>
<pre class="EnlighterJSRAW" data-enlighter-group="" data-enlighter-highlight="1" data-enlighter-language="generic" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-theme="" data-enlighter-title="">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
<pre class="EnlighterJSRAW" data-enlighter-group="" data-enlighter-highlight="" data-enlighter-language="generic" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-theme="" data-enlighter-title="">iex(8)> StarterApp.say("Hallo", "Welt")
Hallo Welt!
:ok
iex(5)>