Em Elixir, a documentação é um cidadão de primeira classe.
Há dois atributos de módulo usados habitualmente para documentar o teu código: @moduledoc para documentar um módulo e @doc para documentar uma função que se segue ao atributo. O atributo @moduledoc aparece normalmente na primeira linha do módulo, e o atributo @doc aparece normalmente mesmo antes da definição de uma função, ou da especificação de tipo da função, se esta existir. A documentação é habitualmente escrita numa string com várias linhas, usando a sintaxe heredoc.
A documentação de Elixir é escrita em Markdown.
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 é uma linguagem de tipagem dinâmica, o que significa que não faz verificações de tipos em tempo de compilação. Ainda assim, as especificações de tipo podem ser usadas como uma forma de documentação.
Uma especificação de tipo pode ser adicionada a uma função com o atributo de módulo @spec, imediatamente antes da definição da função. Depois da @spec vem o nome da função e uma lista dos tipos de todos os seus argumentos, entre parênteses e separada por vírgulas. O tipo do valor devolvido é separado dos argumentos da função por dois pontos duplos ::.
@spec longer_than?(String.t(), non_neg_integer()) :: boolean()
def longer_than?(string, length), do: String.length(string) > length
Os tipos usados mais frequentemente incluem:
boolean()
String.t()
integer(), non_neg_integer(), pos_integer(), float()
list()
any()
Alguns tipos também podem ser parametrizados, por exemplo list(integer) é uma lista de inteiros.
Valores literais também podem ser usados como tipos.
Uma união de tipos pode ser escrita com a barra vertical |. Por exemplo, integer() | :error significa um inteiro ou o átomo literal :error.
Podes encontrar uma lista completa de todos os tipos na secção «Typespecs» da documentação oficial.
Os argumentos na especificação de tipo também podem ter nome, o que é útil para distinguir vários argumentos do mesmo tipo. O nome do argumento, seguido de dois pontos duplos, coloca-se antes do tipo do argumento.
@spec to_hex({hue :: integer, saturation :: integer, lightness :: integer}) :: String.t()
As especificações de tipo não se limitam aos tipos incorporados. Podes definir tipos personalizados com o atributo de módulo @type. A definição de um tipo personalizado começa com o nome do tipo, seguido de dois pontos duplos e depois o próprio tipo.
@type color :: {hue :: integer, saturation :: integer, lightness :: integer}
@spec to_hex(color()) :: String.t()
Um tipo personalizado pode ser usado a partir do mesmo módulo onde está definido, ou a partir de outro módulo.
Trabalhas no gabinete municipal há algum tempo e desenvolviste um conjunto de ferramentas que agilizam o teu trabalho do dia a dia, por exemplo a preencher formulários.
Agora vai chegar um novo colega e apercebeste-te de que as tuas ferramentas podiam não ser autoexplicativas. Há muitas convenções estranhas no teu gabinete, como preencher sempre os formulários com letras maiúsculas e evitar deixar campos vazios.
Decides escrever alguma documentação para que o teu novo colega possa começar logo a usar as tuas ferramentas.
Adiciona ao módulo Form documentação que descreva o seu objetivo. Deve dizer o seguinte:
A collection of loosely related functions helpful for filling out various forms at the city office.
Adiciona documentação e um typespec à função Form.blanks/1. A documentação deve dizer o seguinte:
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.
O typespec deve explicar que a função aceita um único argumento, um número inteiro não negativo, e devolve uma string.
Adiciona documentação e um typespec à função Form.letters/1. A documentação deve dizer o seguinte:
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.
O typespec deve explicar que a função aceita um único argumento, uma string, e devolve uma lista de strings.
Adiciona documentação e um typespec à função Form.check_length/2. A documentação deve dizer o seguinte:
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.
O typespec deve explicar que a função aceita dois argumentos, uma string e um número inteiro não negativo, e devolve um de dois valores possíveis. Devolve ou o átomo :ok ou um tuplo de dois elementos, sendo o primeiro o átomo :error e o segundo um número inteiro positivo.
Por alguma razão que desconheces, o sistema interno do gabinete municipal usa duas formas diferentes de representar endereços: ou como um mapa, ou como um tuplo.
Documenta este facto definindo três tipos públicos personalizados:
address_map - um mapa com as chaves :street, :postal_code e :city. Cada chave contém um valor do tipo string.address_tuple - um tuplo com três valores - street, postal_code e city. Cada valor é do tipo string. Distingue os valores dando-lhes nomes no typespec.address - pode ser um address_map ou um address_tuple.Adiciona documentação e um typespec à função Form.format_address/1. A documentação deve dizer o seguinte:
Formats the address as an uppercase multiline string.
O typespec deve explicar que a função aceita um argumento, um endereço, e devolve uma string.
Inscreve-te no Exercism para aprenderes e dominares Elixir com 58 conceitos168 exercícios, e mentoria humana real, tudo grátis.