Tracks
/
Elixir
Elixir
/
Lehrplan
/
Typespecs
Ty

Typespecs in Elixir

1 Übung

Über Typespecs

Elixir ist eine dynamisch typisierte Sprache. Das bedeutet, dass sie keine Typprüfungen zur Kompilierzeit bietet. Trotzdem können Typspezifikationen nützlich sein, denn sie:

  • Dienen als Dokumentation.
  • Können von statischen Analysetools wie Dialyzer genutzt werden, um mögliche Fehler zu finden.

Eine Typspezifikation kannst du einer Funktion mit dem Modulattribut @spec direkt vor der Funktionsdefinition hinzufügen.

@spec longer_than?(String.t(), non_neg_integer()) :: boolean()
def longer_than?(string, length), do: String.length(string) > length

Typen

Ausdrücke, die in einer Typspezifikation erlaubt sind:

  • Grundlegende Typen, zum Beispiel:

    • boolean()
    • integer(), non_neg_integer(), pos_integer(), float()
    • list()
    • map()
    • any()
  • Eine Vereinigung von Typen:

    • z. B. integer() | list(integer())
  • Parametrisierte Typen:

    • z. B. list(integer()) - eine Liste von Ganzzahlen
    • z. B. %{age: integer()} - Map mit einer Ganzzahl als Wert unter dem Schlüssel :age
  • Remote-Typen (die in einem Modul definiert sind), zum Beispiel:

  • Literale, zum Beispiel:

    • %{} - eine leere Map
    • [] - eine leere Liste (aber [any()] ist eine nicht leere Liste)
    • z. B. :ok - ein Atom-Literal
  • Eingebaute spezialisierte Typen, zum Beispiel:

    • char() - eine Ganzzahl aus dem Bereich 0..0x10FFFF
    • charlist() - eine Liste von Chars
    • keyword() - eine Liste von Tupeln mit zwei Elementen, wobei das erste Element ein Atom ist
  • Benutzerdefinierte Typen

Eine vollständige Liste aller Typen findest du im Abschnitt "Typespecs" der offiziellen Dokumentation.

Argumente benennen

Argumente in der Typspezifikation können auch benannt werden. Das ist nützlich, um mehrere Argumente desselben Typs zu unterscheiden.

@spec to_hex({hue :: integer, saturation :: integer, lightness :: integer}) :: String.t()

Benutzerdefinierte Typen

Benutzerdefinierte Typen kannst du mit einem der drei Modulattribute definieren:

  • @type - definiert einen öffentlichen Typ
  • @typep - definiert einen privaten Typ
  • @opaque - definiert einen öffentlichen Typ, dessen Struktur privat ist
@type color :: {hue :: integer, saturation :: integer, lightness :: integer}

@spec to_hex(color()) :: String.t()

String.t() vs binary() vs string()

String.t() ist der richtige Typ für Elixir-Strings, also UTF-8-kodierte Binaries. Technisch gesehen ist String.t() als binary() definiert, und für Analysetools sind diese beiden Typen gleichwertig. Für die Dokumentation von Funktionen, die mit Elixir-Strings arbeiten, ist String.t() jedoch die Wahl, die die Absicht besser erkennen lässt.

Andererseits ist string() ein anderer Typ. Es ist ein Erlang-String, in Elixir als Charlist bekannt. Der Typ string() sollte in Typspezifikationen vermieden werden; stattdessen solltest du charlist() verwenden.

Dialyzer

Dialyzer ist ein statisches Analysetool, das Probleme wie Typfehler in Erlang- und Elixir-Code erkennen kann. Der einfachste Weg, Dialyzer in einem Elixir-Projekt zu nutzen, ist Dialyxir.

Über GitHub bearbeiten Der Link öffnet sich in einem neuen Fenster oder Tab

Lerne Typespecs