Ty

Typespecs em Elixir

1 exercício

Sobre Typespecs

O Elixir é uma linguagem com tipos dinâmicos, 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 úteis porque:

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

Podes adicionar uma especificação de tipo a uma função com o atributo de módulo @spec, mesmo 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 numa 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()}, um 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 incorporados, por exemplo:

    • char(), um inteiro do intervalo 0..0x10FFFF
    • charlist(), uma lista de carateres
    • keyword(), uma lista de tuplos de dois elementos, em que o primeiro elemento é um átomo
  • Tipos personalizados

Encontras a lista completa de todos os tipos na secção "Typespecs" da documentação oficial.

Dar nomes aos argumentos

Os argumentos da especificação de tipo também podem ter nomes, o que é útil para distinguir vários argumentos do mesmo tipo.

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

Tipos personalizados

Podes definir tipos personalizados com um destes 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()

O String.t() é o tipo correto a usar para as strings do Elixir, que são binários codificados em UTF-8. Tecnicamente, String.t() é definido como um binary(), e esses dois tipos são equivalentes para as ferramentas de análise, mas 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 nas especificações de tipo; em vez dele, deves usar charlist().

Dialyzer

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

Editar via GitHub A ligação abre numa nova janela ou separador

Aprende Typespecs