In Elixir ist Dokumentation ein zentraler Bestandteil der Sprache.
Es gibt zwei Modulattribute, mit denen du deinen Code üblicherweise dokumentierst: @moduledoc für die Dokumentation eines Moduls und @doc für die Dokumentation einer Funktion, die auf das Attribut folgt. Das Attribut @moduledoc steht meist in der ersten Zeile des Moduls, und @doc steht meist direkt vor einer Funktionsdefinition oder vor der Typspezifikation der Funktion, falls sie eine hat. Die Dokumentation wird üblicherweise in einem mehrzeiligen String mit der Heredoc-Syntax geschrieben.
Elixir-Dokumentation wird in Markdown geschrieben.
defmodule String do
@moduledoc """
Strings in Elixir are UTF-8 encoded binaries.
"""
@doc """
Converts all characters in the given string to uppercase according to `mode`.
## Examples
iex> String.upcase("abcd")
"ABCD"
iex> String.upcase("olá")
"OLÁ"
"""
def upcase(string, mode \\ :default)
end
Elixir ist eine dynamisch typisierte Sprache. Das bedeutet, dass sie keine Typprüfung zur Kompilierzeit bietet. Trotzdem kannst du Typspezifikationen als eine Form der Dokumentation verwenden.
Du kannst einer Funktion eine Typspezifikation hinzufügen, indem du das Modulattribut @spec direkt vor die Funktionsdefinition schreibst. Auf @spec folgen der Funktionsname und, in runden Klammern, eine durch Kommas getrennte Liste aller Typen seiner Argumente. Der Typ des Rückgabewerts wird mit einem doppelten Doppelpunkt :: von den Argumenten der Funktion getrennt.
@spec longer_than?(String.t(), non_neg_integer()) :: boolean()
def longer_than?(string, length), do: String.length(string) > length
Zu den am häufigsten verwendeten Typen gehören:
boolean()
String.t()
integer(), non_neg_integer(), pos_integer(), float()
list()
any()
Einige Typen können auch parametrisiert werden. list(integer) ist zum Beispiel eine Liste von Ganzzahlen.
Auch Literalwerte kannst du als Typen verwenden.
Eine Vereinigung von Typen schreibst du mit dem Pipe-Zeichen |. integer() | :error bedeutet zum Beispiel entweder eine Ganzzahl oder das Atom-Literal :error.
Eine vollständige Liste aller Typen findest du im Abschnitt „Typespecs" in der offiziellen Dokumentation.
Argumente in der Typspezifikation kannst du auch benennen. Das ist nützlich, um mehrere Argumente desselben Typs zu unterscheiden. Der Argumentname steht, gefolgt von einem doppelten Doppelpunkt, vor dem Typ des Arguments.
@spec to_hex({hue :: integer, saturation :: integer, lightness :: integer}) :: String.t()
Typspezifikationen sind nicht auf die eingebauten Typen beschränkt. Eigene Typen definierst du mit dem Modulattribut @type. Eine Definition eines eigenen Typs beginnt mit dem Namen des Typs, gefolgt von einem doppelten Doppelpunkt und dann dem Typ selbst.
@type color :: {hue :: integer, saturation :: integer, lightness :: integer}
@spec to_hex(color()) :: String.t()
Einen eigenen Typ kannst du im selben Modul verwenden, in dem er definiert ist, oder in einem anderen Modul.
Du arbeitest seit einer Weile im Stadtbüro und hast eine Reihe von Tools entwickelt, die deine tägliche Arbeit beschleunigen, zum Beispiel das Ausfüllen von Formularen.
Jetzt kommt eine neue Kollegin zu dir ins Team, und dir ist aufgefallen, dass deine Tools vielleicht nicht selbsterklärend sind. In deinem Büro gibt es viele seltsame Konventionen, zum Beispiel Formulare immer in Großbuchstaben auszufüllen und Felder nicht leer zu lassen.
Du beschließt, eine Dokumentation zu schreiben, damit deine neue Kollegin direkt einsteigen und deine Tools nutzen kann.
Füge dem Modul Form eine Dokumentation hinzu, die seinen Zweck beschreibt. Sie sollte so lauten:
A collection of loosely related functions helpful for filling out various forms at the city office.
Füge der Funktion Form.blanks/1 eine Dokumentation und eine Typspezifikation hinzu. Die Dokumentation sollte so lauten:
Generates a string of a given length.
This string can be used to fill out a form field that is supposed to have no value.
Such fields cannot be left empty because a malicious third party could fill them out with false data.
Die Typspezifikation sollte erklären, dass die Funktion ein einziges Argument akzeptiert, eine nicht-negative Ganzzahl, und einen String zurückgibt.
Füge der Funktion Form.letters/1 eine Dokumentation und eine Typspezifikation hinzu. Die Dokumentation sollte so lauten:
Splits the string into a list of uppercase letters.
This is needed for form fields that don't offer a single input for the whole string,
but instead require splitting the string into a predefined number of single-letter inputs.
Die Typspezifikation sollte erklären, dass die Funktion ein einziges Argument akzeptiert, einen String, und eine Liste von Strings zurückgibt.
Füge der Funktion Form.check_length/2 eine Dokumentation und eine Typspezifikation hinzu. Die Dokumentation sollte so lauten:
Checks if the value has no more than the maximum allowed number of letters.
This is needed to check that the values of fields do not exceed the maximum allowed length.
It also tells you by how much the value exceeds the maximum.
Die Typspezifikation sollte erklären, dass die Funktion zwei Argumente akzeptiert, einen String und eine nicht-negative Ganzzahl, und einen von zwei möglichen Werten zurückgibt. Sie gibt entweder das Atom :ok zurück oder ein 2-Tupel, dessen erstes Element das Atom :error ist und dessen zweites eine positive Ganzzahl ist.
Aus dir unbekannten Gründen verwendet das interne System des Stadtbüros zwei verschiedene Arten, Adressen darzustellen: entweder als Map oder als Tupel.
Dokumentiere diese Tatsache, indem du drei eigene öffentliche Typen definierst:
address_map: eine Map mit den Schlüsseln :street, :postal_code und :city. Jeder Schlüssel enthält einen Wert vom Typ String.address_tuple: ein Tupel mit drei Werten, nämlich street, postal_code und city. Jeder Wert ist vom Typ String. Unterscheide die Werte, indem du ihnen in der Typspezifikation Namen gibst.address: kann entweder eine address_map oder eine address_tuple sein.Füge der Funktion Form.format_address/1 eine Dokumentation und eine Typspezifikation hinzu. Die Dokumentation sollte so lauten:
Formats the address as an uppercase multiline string.
Die Typspezifikation sollte erklären, dass die Funktion ein Argument akzeptiert, eine Adresse, und einen String zurückgibt.
Melde dich bei Exercism an, um Elixir mit 58 Konzepte168 Übungen und echtem menschlichen Mentoring zu lernen und zu meistern, alles kostenlos.