Ty

Typespecs em Elixir

1 exercício

Sobre Typespecs

Elixir é uma linguagem de tipagem dinâmica, o que significa que ela não faz verificações de tipos em tempo de compilação. Mesmo assim, as especificações de tipo podem ser úteis porque:

  • Servem como documentação.
  • Podem ser usadas por ferramentas de análise estática como o Dialyzer para encontrar possíveis bugs.

Você pode adicionar uma especificação de tipo a uma função usando o atributo de módulo @spec logo antes da definição da função.

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

Tipos

Expressões permitidas em uma especificação de tipo:

  • Tipos básicos, por exemplo:

    • boolean()
    • integer(), non_neg_integer(), pos_integer(), float()
    • list()
    • map()
    • any()
  • Uma união de tipos:

    • por exemplo, integer() | list(integer())
  • Tipos parametrizados:

    • por exemplo, list(integer()) - uma lista de inteiros
    • por exemplo, %{age: integer()} - mapa com um valor inteiro sob a chave :age
  • Tipos remotos (definidos em algum módulo), por exemplo:

  • Literais, por exemplo:

    • %{} - um mapa vazio
    • [] - uma lista vazia (mas [any()] é uma lista não vazia)
    • por exemplo, :ok - um literal de átomo
  • Tipos especializados embutidos, por exemplo:

    • char() - um inteiro do intervalo 0..0x10FFFF
    • charlist() - uma lista de chars
    • keyword() - uma lista de tuplas de dois elementos, onde o primeiro elemento é um átomo
  • Tipos personalizados

Uma lista completa de todos os tipos está na seção "Typespecs" da documentação oficial.

Nomeando argumentos

Os argumentos na especificação de tipo também podem ser nomeados. Isso é útil para distinguir vários argumentos do mesmo tipo.

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

Tipos personalizados

Tipos personalizados podem ser definidos com um dos três atributos de módulo:

  • @type - define um tipo público
  • @typep - define um tipo privado
  • @opaque - define um tipo público cuja estrutura é privada
@type color :: {hue :: integer, saturation :: integer, lightness :: integer}

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

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

String.t() é o tipo correto a usar para strings do Elixir, que são binários codificados em UTF-8. Tecnicamente, String.t() é definida como um binary(), e esses dois tipos são equivalentes para as ferramentas de análise. Ainda assim, String.t() é a escolha que revela melhor a intenção ao documentar funções que trabalham com strings do Elixir.

Por outro lado, string() é um tipo diferente. É uma string do Erlang, conhecida no Elixir como charlist. O tipo string() deve ser evitado em especificações de tipo, e charlist() deve ser usado no lugar.

Dialyzer

O Dialyzer é uma ferramenta de análise estática que consegue detectar problemas como erros de tipo em código Erlang e Elixir. A forma mais fácil de usar o Dialyzer em um projeto Elixir é com o Dialyxir.

Editar via GitHub O link abre em uma nova janela ou aba

Aprenda Typespecs