Funktional, nebenläufig und ausfallsicher
Ausführen: Interaktiv mit iex, ein Skript hallo.exs mit elixir hallo.exs, Projekte mit mix new demo und mix run.
iex zum Ausprobieren, .exs-Skripte mit elixirIO.puts und IO.inspect zur Ausgabe, #{} in Stringsiex # interaktive Shell (beenden mit Strg+C zweimal)
elixir hallo.exs # Skript ausführen
mix new projekt # neues Projekt
mix run # Projekt starten<> verkettet, String.* für Funktionen= ist der Match-Operator; mit ^ pinnst du Werte[kopf | rest], Tupel {:ok, wert}, Maps %{key: value}Map.put, %{m | key: v} liefern neue WerteIO.inspect({1, "zwei", :drei, 4.0, [5], {6}})
IO.inspect(is_integer(5))
IO.inspect(is_atom(:ok))
IO.inspect(is_binary("text"))
IO.inspect(is_list([1]))
IO.inspect(is_map(%{}))
IO.inspect(is_function(fn -> 1 end))
IO.inspect(round(3.5))
IO.inspect(trunc(3.9))
IO.inspect(max(3, 9))
IO.inspect(abs(-5))
IO.inspect(:erlang.float_to_binary(3.14159, decimals: 2))
IO.inspect(1 == 1.0)
IO.inspect(1 === 1.0)
IO.inspect("abc" < "abd")
IO.inspect(:a < :b)
IO.inspect(Integer.parse("42abc"))
IO.inspect(String.to_integer("7") + 1)
IO.inspect(1_000_000 * 1_000_000 * 1_000_000)fn x -> ... end (Aufruf mit .), Capture &(&1 + 1)def/defp, mehrere Klauseln mit Pattern Matching und Guards (when)\\, Funktionen werden über Name/Stelligkeit identifiziert|> reicht Ergebnisse als erstes Argument weiteralias, import, @doc, @specquadrat = fn x -> x * x end
plus = fn a, b -> a + b end
IO.inspect(quadrat.(7)) # Aufruf mit Punkt!
IO.inspect(plus.(2, 3))
kurz = &(&1 * 2) # Capture-Syntax: &1 ist das erste Argument
IO.inspect(kurz.(21))
IO.inspect(Enum.map([1, 2, 3], &(&1 + 10)))
IO.inspect(Enum.map(["a", "b"], &String.upcase/1)) # benannte Funktion als Wert
beschreibe = fn
0 -> "null"
n when n < 0 -> "negativ"
n when rem(n, 2) == 0 -> "gerade"
_ -> "ungerade"
end
IO.inspect(Enum.map([0, -3, 4, 7], beschreibe))
faktor = 3
mal = fn x -> x * faktor end # Closure fängt Variablen ein
faktor = 100
IO.inspect(mal.(5))if, unless, cond, case, with sind Ausdrücke; case und with nutzen Pattern MatchingEnum.map/filter/reduce/... und for-ComprehensionsStream ist lazy, ideal für große oder unendliche Folgenx = 7
if x > 5 do
IO.puts("groß")
else
IO.puts("klein")
end
unless x == 0, do: IO.puts("nicht null")
# cond: wie if/else-if
note =
cond do
x >= 9 -> "sehr gut"
x >= 5 -> "gut"
true -> "weiter üben"
end
IO.puts(note)
# case: Pattern Matching mit Guards
beschreibe = fn wert ->
case wert do
{:ok, n} when n > 100 -> "großer Erfolg #{n}"
{:ok, n} -> "Erfolg #{n}"
{:error, grund} -> "Fehler: #{grund}"
[] -> "leere Liste"
[h | _] -> "Liste mit Kopf #{h}"
%{name: name} -> "Map mit Name #{name}"
_ -> "unbekannt"
end
end
for v <- [{:ok, 500}, {:ok, 5}, {:error, "kaputt"}, [], [9, 8], %{name: "Mia"}, 3.5], do: IO.puts(beschreibe.(v))
# with: Kette von Matches, bricht beim ersten Fehlschlag ab
def_parse = fn s ->
with {n, ""} <- Integer.parse(s),
true <- n > 0 do
{:ok, n * 2}
else
:error -> {:error, :keine_zahl}
{_, _rest} -> {:error, :ungueltig}
false -> {:error, :nicht_positiv}
end
end
IO.inspect(Enum.map(["21", "abc", "-5", "7x"], def_parse))defstruct für Datentypen mit festen Feldern; Pattern Matching %Person{name: n}defprotocol/defimpl) für Polymorphie nach Typ, Behaviours (@callback) als Modulvertrag{:ok, wert} / {:error, grund}; try/rescue/after, raise, defexception!-Funktionen werfen, normale Varianten liefern Tupeldefmodule Person do
@enforce_keys [:name]
defstruct [:name, alter: 0, hobbys: []]
def neu(name, alter), do: %Person{name: name, alter: alter}
def vorstellen(%Person{name: n, alter: a}) when a >= 18, do: "#{n} (#{a}, volljährig)"
def vorstellen(%Person{name: n, alter: a}), do: "#{n} (#{a})"
def geburtstag(%Person{} = p), do: %{p | alter: p.alter + 1}
end
defmodule Demo1 do
def run do
mia = Person.neu("Mia", 17)
IO.inspect(mia)
IO.puts(Person.vorstellen(mia))
aelter = Person.geburtstag(mia)
IO.puts(Person.vorstellen(aelter))
IO.inspect(%Person{name: "Tom"})
IO.inspect(mia.name)
IO.inspect(is_map(mia))
IO.inspect(mia.__struct__)
IO.inspect(Map.from_struct(mia))
IO.inspect(match?(%Person{alter: 17}, mia))
try do
struct!(Person, alter: 5)
rescue
e in ArgumentError -> IO.puts("Pflichtfeld fehlt: #{Exception.message(e)}")
end
end
end
Demo1.run()send/receive)Task für nebenläufige Berechnungen, Agent für einfachen ZustandGenServer für Server mit Zustand: call (synchron), cast (asynchron)pid = spawn(fn -> IO.puts("Hallo aus einem Prozess") end)
Process.sleep(50)
IO.inspect(is_pid(pid))
IO.inspect(Process.alive?(pid))
eltern = self()
spawn(fn -> send(eltern, {:ergebnis, 6 * 7}) end)
receive do
{:ergebnis, wert} -> IO.puts("Antwort: #{wert}")
after
1000 -> IO.puts("Timeout")
end
# Ein Prozess, der Nachrichten verarbeitet
defmodule Echo do
def schleife do
receive do
{:echo, absender, text} ->
send(absender, {:antwort, String.upcase(text)})
schleife()
:stop ->
:ok
end
end
end
echo = spawn(fn -> Echo.schleife() end)
send(echo, {:echo, self(), "hallo"})
receive do
{:antwort, text} -> IO.puts(text)
end
send(echo, :stop)mix new, mix deps.get, mix test, mix format sind die Alltagsbefehleassert, describe, setup; Doctests testen die Dokumentationmix new meine_app # Projekt mit Struktur
cd meine_app
mix deps.get # Abhängigkeiten holen
mix compile
mix run -e 'IO.puts("hi")'
mix test # Tests
mix format # Code formatieren
iex -S mix # iex mit geladenem Projekt
mix help # alle Aufgabenx = 1 Zuweisung/Match ^x Pin
{a, b} = {1, 2} Tupel [h | t] = [1, 2, 3]
%{k: v} = map Map-Match %Struct{f: v}
fn x -> x end anonyme Funktion &(&1 + 1) Capture
def f(x) when x > 0, do: … Funktion mit Guard defp privat
x |> f() |> g(1) Pipe
case v do pattern -> … end if/unless/cond/with
for x <- l, cond, do: … Comprehension