A documentação no Elixir é um cidadão de primeira classe.
Existem dois atributos de módulo comumente usados para documentar seu código: @moduledoc para documentar um módulo e @doc para documentar uma função que vem logo depois do atributo. O atributo @moduledoc geralmente aparece na primeira linha do módulo, e o atributo @doc geralmente aparece logo antes da definição de uma função, ou da especificação de tipo da função, se ela tiver uma. A documentação costuma ser escrita em uma string multilinha usando a sintaxe heredoc.
A documentação do 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
O Elixir é uma linguagem de tipagem dinâmica, o que significa que ela não faz verificação de tipos em tempo de compilação. Ainda assim, 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 usando o atributo de módulo @spec logo antes da definição da função. @spec é seguido pelo nome da função e por uma lista com os tipos de todos os seus argumentos, entre parênteses, separados por vírgulas. O tipo do valor de retorno é separado dos argumentos da função por um dois-pontos duplo ::.
@spec longer_than?(String.t(), non_neg_integer()) :: boolean()
def longer_than?(string, length), do: String.length(string) > length
Os tipos mais usados 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 usando a barra vertical |. Por exemplo, integer() | :error significa ou um inteiro, ou o literal de átomo :error.
Uma lista completa de todos os tipos pode ser encontrada na seção "Typespecs" da documentação oficial.
Os argumentos na especificação de tipo também podem ser nomeados, o que é útil para distinguir vários argumentos do mesmo tipo. O nome do argumento, seguido de um dois-pontos duplo, vem 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 nativos. Tipos personalizados podem ser definidos usando o atributo de módulo @type. A definição de um tipo personalizado começa com o nome do tipo, seguido de um dois-pontos duplo 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 tanto no mesmo módulo onde foi definido quanto em outro módulo.
Você trabalha no escritório da cidade há um tempo e desenvolveu um conjunto de ferramentas que agilizam seu dia a dia, por exemplo para preencher formulários.
Agora, um novo colega está chegando, e você percebeu que suas ferramentas podem não ser autoexplicativas. Há muitas convenções estranhas no seu escritório, como sempre preencher formulários com letras maiúsculas e evitar deixar campos vazios.
Você decide escrever uma documentação para que seu novo colega possa começar a usar suas ferramentas rapidamente.
Adicione documentação ao módulo Form que descreva seu propósito. Ela deve dizer:
A collection of loosely related functions helpful for filling out various forms at the city office.
Adicione documentação e um typespec à função Form.blanks/1. A documentação deve dizer:
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 retorna uma string.
Adicione documentação e um typespec à função Form.letters/1. A documentação deve dizer:
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 retorna uma lista de strings.
Adicione documentação e um typespec à função Form.check_length/2. A documentação deve dizer:
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 retorna um de dois valores possíveis. Ela retorna ou o átomo :ok ou uma tupla de 2 elementos, em que o primeiro elemento é o átomo :error e o segundo é um número inteiro positivo.
Por algum motivo que você desconhece, o sistema interno do escritório da cidade usa duas formas diferentes de representar endereços: como um mapa ou como uma tupla.
Documente esse fato definindo três tipos públicos personalizados:
address_map: um mapa com as chaves :street, :postal_code e :city. Cada chave guarda um valor do tipo string.address_tuple: uma tupla com três valores, street, postal_code e city. Cada valor é do tipo string. Diferencie os valores dando nomes a eles no typespec.address: pode ser um address_map ou um address_tuple.Adicione documentação e um typespec à função Form.format_address/1. A documentação deve dizer:
Formats the address as an uppercase multiline string.
O typespec deve explicar que a função aceita um argumento, um endereço, e retorna uma string.
Crie sua conta no Exercism para aprender e dominar Elixir com 58 conceitos168 exercícios e mentoria humana de verdade, tudo de graça.