forked from elixirschool/elixirschool
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Started translation (elixirschool#738)
- Loading branch information
1 parent
122e5b5
commit 99f23a2
Showing
20 changed files
with
2,839 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
--- | ||
layout: page | ||
title: Elixir School | ||
lang: de | ||
--- | ||
|
||
[![Lizenz](http:https://img.shields.io/badge/license-MIT-brightgreen.svg)](http:https://opensource.org/licenses/MIT) | ||
|
||
Lektionen zur Elixir Programmiersprache, inspiriert durch Twitters [Scala School](http:https://twitter.github.io/scala_school/). | ||
|
||
_Dein Feedback und Unterstützung ist sehr gern gesehen!_ | ||
|
||
## Über Elixir | ||
|
||
"Elixir ist eine dynamische funktionale Sprache entworfen für skalierbare und wartbare Anwendungen." — [elixir-lang.org](http:https://elixir-lang.org/) | ||
|
||
Elixir benutzt die battle tested Erlang VM (BEAM) um verteilte und fehlertolerante Systeme mit wenig Latenz out of the box zu ermöglichen. | ||
|
||
__Features__: | ||
|
||
+ Skalierbar | ||
+ Fehlertolerant | ||
+ Funktionale Programmierung | ||
+ Erweiterbar |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
--- | ||
layout: page | ||
title: Nebenläufigkeit | ||
category: advanced | ||
order: 4 | ||
lang: de | ||
--- | ||
|
||
Einer der wichtigsten Gründe für den Einsatz von Elixir ist die eingebaute Unterstützung von Nebenläufigkeit. Dank der Erlang VM (BEAM) ist Nebenläufigkeit in Elixir einfacher als erwartet. Die Nebenläufigkeit basiert auf dem Actor Model, bei dem ein abgeschlossener Prozess mit anderen Prozessen durch message passing kommuniziert. | ||
|
||
In dieser Lektion werden wir uns die Nebenläufigkeitsmodule anschauen, welche mit Elixir geliefert werden. Im darauf folgenden Kapitel werden wir OTP behandeln, die diese Module implementieren. | ||
|
||
{% include toc.html %} | ||
|
||
## Prozesse | ||
|
||
Prozesse in der Erlang VM sind leichtgewichtig und laufen verteilt auf allen CPUs. Während sie wie native Threads wirken sind sie simpler und es ist nicht ungewöhnlich mehrere Tausend nebenläufige Prozesse in einer Elixiranwendung zu haben. | ||
|
||
Der einfachste Weg einen Prozess zu erzeugen ist durch `spawn`, welche eine anonyme oder benannte Funktion entgegen nimmt. Wenn wir einen neuen Prozess erzeugen gibt uns dieser einen _Process Identifier_, auch PID genannt, zurück, welcher den Prozess in unserer Anwendung eindeutig identifiziert. | ||
|
||
Für den Start schreiben wir ein Modul und definieren eine Funktion, die wir gerne laufen lassen würden: | ||
|
||
```elixir | ||
defmodule Example do | ||
def add(a, b) do | ||
IO.puts(a + b) | ||
end | ||
end | ||
|
||
iex> Example.add(2, 3) | ||
5 | ||
:ok | ||
``` | ||
|
||
Um die Funktion asynchron auszuwerten benutzen wir `spawn/3`: | ||
|
||
```elixir | ||
iex> spawn(Example, :add, [2, 3]) | ||
5 | ||
#PID<0.80.0> | ||
``` | ||
|
||
### Message Passing | ||
|
||
Zur Kommunikation benutzen Prozesse message passing. Zwei Komponenten werden hierfür benötigt: `send/2` und `receive`. Die Funktion `send/2` erlaubt uns Nachrichten an PIDs zu schicken. Auf der anderen Seite nutzen wir `receive` um Nachrichten zu empfangen. Falls keine Übereinstimmung gefunden wird läuft die Ausführung ohne Unterbrechung weiter. | ||
|
||
```elixir | ||
defmodule Example do | ||
def listen do | ||
receive do | ||
{:ok, "hello"} -> IO.puts "World" | ||
end | ||
|
||
listen | ||
end | ||
end | ||
|
||
iex> pid = spawn(Example, :listen, []) | ||
#PID<0.108.0> | ||
|
||
iex> send pid, {:ok, "hello"} | ||
World | ||
{:ok, "hello"} | ||
|
||
iex> send pid, :ok | ||
:ok | ||
``` | ||
|
||
Bei genauer Betrachtung des Codes fällt auf, dass die `listen/0` Funktion rekursiv ist, was unserem Prozess erlaubt mehrere Nachrichten zu empfangen. Ohne Rekursion würde unser Prozess einfach beendet werden, nachdem die erste Nachricht ausgewertet wurde. | ||
|
||
### Kopplung von Prozessen | ||
|
||
Ein Problem mit `spawn` ist mitzubekommen, wenn ein Prozess abgestürzt ist. Dafür müssen wir unsere Prozesse mit `spawn_link` verbinden. Zwei auf diese Art verbundene Prozesse bekommen mit, sollte der andere abstürzen: | ||
|
||
```elixir | ||
defmodule Example do | ||
def explode, do: exit(:kaboom) | ||
end | ||
|
||
iex> spawn(Example, :explode, []) | ||
#PID<0.66.0> | ||
|
||
iex> spawn_link(Example, :explode, []) | ||
** (EXIT from #PID<0.57.0>) :kaboom | ||
``` | ||
|
||
Manchmal wollen wir nicht, dass ein abgestürzter Prozess den aktuelle Prozess zum abstürzen bringt. Dafür müssen wir die exits abfangen. Beim Abfangen von exits werden sie als Nachrichtentupel empfangen: `{:EXIT, from_pid, reason}`. | ||
|
||
```elixir | ||
defmodule Example do | ||
def explode, do: exit(:kaboom) | ||
def run do | ||
Process.flag(:trap_exit, true) | ||
spawn_link(Example, :explode, []) | ||
|
||
receive do | ||
{:EXIT, from_pid, reason} -> IO.puts "Exit reason: #{reason}" | ||
end | ||
end | ||
end | ||
|
||
iex> Example.run | ||
Exit reason: kaboom | ||
:ok | ||
``` | ||
|
||
### Monitoring von Prozessen | ||
|
||
Was wenn wir zwei Prozesse nicht verbinden wollen, aber dennoch informiert werden? Dafür können wir Prozesse mit `spawn_monitor` überwachen. Wenn wir einen Prozess überwachen bekommen wir eine Nachricht falls der Prozess abstürzt, ohne dass unser aktueller Prozess mitabstürzt. Ebensowenig müssen wir dazu exits abfangen. | ||
|
||
```elixir | ||
defmodule Example do | ||
def explode, do: exit(:kaboom) | ||
def run do | ||
{pid, ref} = spawn_monitor(Example, :explode, []) | ||
|
||
receive do | ||
{:DOWN, ref, :process, from_pid, reason} -> IO.puts "Exit reason: #{reason}" | ||
end | ||
end | ||
end | ||
|
||
iex> Example.run | ||
Exit reason: kaboom | ||
:ok | ||
``` | ||
|
||
## Agenten | ||
|
||
Agenten sind eine Abstraktion über Hintergrundprozesse welche einen Zustand beibehalten. Wir können sie von anderen Prozessen innerhalb unserer Anwendung und aller Knoten abrufen. Der Zustand unserer Agenten wird auf den Rückgabewert unserer Funktion gesetzt: | ||
|
||
```elixir | ||
iex> {:ok, agent} = Agent.start_link(fn -> [1, 2, 3] end) | ||
{:ok, #PID<0.65.0>} | ||
|
||
iex> Agent.update(agent, fn (state) -> state ++ [4, 5] end) | ||
:ok | ||
|
||
iex> Agent.get(agent, &(&1)) | ||
[1, 2, 3, 4, 5] | ||
``` | ||
|
||
Durch Benennung eines Agenten können wir diesen direkt ansprechen, anstatt auf dessen PID zurückgreifen zu müssen: | ||
|
||
```elixir | ||
iex> Agent.start_link(fn -> [1, 2, 3] end, name: Numbers) | ||
{:ok, #PID<0.74.0>} | ||
|
||
iex> Agent.get(Numbers, &(&1)) | ||
[1, 2, 3] | ||
``` | ||
|
||
## Tasks | ||
|
||
Tasks erlauben eine Funktion im Hintergrund auszuführen und deren Rückgabewert später zu erhalten. Sie sind besonders nützlich wenn aufwendige Berechnungen durchgeführt werden, ohne die Ausführung der Anwendung zu blockieren. | ||
|
||
```elixir | ||
defmodule Example do | ||
def double(x) do | ||
:timer.sleep(2000) | ||
x * 2 | ||
end | ||
end | ||
|
||
iex> task = Task.async(Example, :double, [2000]) | ||
%Task{pid: #PID<0.111.0>, ref: #Reference<0.0.8.200>} | ||
|
||
# Führe langwierige Berechnung durch | ||
|
||
iex> Task.await(task) | ||
4000 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
--- | ||
layout: page | ||
title: Ausführbare Dateien | ||
category: advanced | ||
order: 3 | ||
lang: de | ||
--- | ||
|
||
Um ausführbare Dateien in Elixir zu erstellen werden wir escript benutzen. Escript erzeugt eine ausführbare Datei, welche auf jedem System lauffähig ist, auf dem Erlang installiert ist. | ||
|
||
{% include toc.html %} | ||
|
||
## Einstieg | ||
|
||
Um eine ausführbare Datei mit escript zu erzeugen gibt es nur wenige Dinge zu tun: Eine `main/1` Methode erzeugen und das Mixfile anpassen. | ||
|
||
Wir starten mit der Erzeugung eines Moduls, welches als Einstiegspunkt für unsere ausführbare Datei dienen soll. Dort werden wir `main/1` implementieren: | ||
|
||
```elixir | ||
defmodule ExampleApp.CLI do | ||
def main(args \\ []) do | ||
# Mache irgendetwas | ||
end | ||
end | ||
``` | ||
|
||
Als nächstes müssen wir für unser Projekt in unserem Mixfile die `:escript` Option aufnehmen. Dazu müssen wir noch ein `:main_module` festlegen: | ||
|
||
```elixir | ||
defmodule ExampleApp.Mixfile do | ||
def project do | ||
[app: :example_app, | ||
version: "0.0.1", | ||
escript: escript] | ||
end | ||
|
||
def escript do | ||
[main_module: ExampleApp.CLI] | ||
end | ||
end | ||
``` | ||
|
||
## Argumente parsen | ||
|
||
Mit unserer erstellten Anwendung können wir dazu übergehen Kommandozeilenargumente zu parsen. Um das zu tun werden wir Elixirs `OptionParser.parse/2` mit der `:switches` Option nutzen, um darauf hinzuweisen, dass unser flag boolean ist: | ||
|
||
```elixir | ||
defmodule ExampleApp.CLI do | ||
def main(args \\ []) do | ||
args | ||
|> parse_args | ||
|> response | ||
|> IO.puts | ||
end | ||
|
||
defp parse_args(args) do | ||
{opts, word, _} = | ||
args | ||
|> OptionParser.parse(switches: [upcase: :boolean]) | ||
|
||
{opts, List.to_string(word)} | ||
end | ||
|
||
defp response({opts, word}) do | ||
if opts[:upcase], do: word = String.upcase(word) | ||
word | ||
end | ||
end | ||
``` | ||
|
||
## Erstellen | ||
|
||
Sobald wir die Konfiguration unserer Anwendung mit escript abgeschlossen haben ist die Erzeugung einer ausführbaren Datei mit der Hilfe von Mix einfach: | ||
|
||
```elixir | ||
$ mix escript.build | ||
``` | ||
|
||
Let's take it for a spin: | ||
|
||
```elixir | ||
$ ./example_app --upcase Hello | ||
HELLO | ||
|
||
$ ./example_app Hi | ||
Hi | ||
``` | ||
|
||
Das wars. Wir haben unsere erste ausführbare Datei in Elixir mit escript erzeugt. |
Oops, something went wrong.