Quando comecei a escrever a nota " Tipos onde não eram esperados ", parecia-me que havia dominado a trazer os tipos Erlang para o tempo de execução e agora posso usá-los no código do cliente no elixir. Haha, como eu era ingênuo.
Qualquer coisa sugerida pelo link funcionará para definições de tipo de local de uso explícitas, como use Foo, var: type(). Infelizmente, essa abordagem está condenada se quisermos definir tipos em outro lugar: próximo a ele no código usando atributos de módulo, ou, ali, na configuração. Por exemplo, para definir uma estrutura, podemos querer escrever algo assim:
# @fields [foo: 42]
# defstruct @fields
@definition var: atom()
use Foo, @definition

O código acima não é que ele não manipulará o tipo da maneira que queremos - ele não coletará de forma alguma porque @definition var: atom()lançará uma exceção ** (CompileError) undefined function atom/0.
Abordagem ingênua
— « » ( @tsilb, , .) , , , , — .
, , __using__/1: , ( field → type()), — , , , {Module, :type, [params]}. ~q||, , , , AST. quote/1 : foo: ~q|atom()|. , , . . , - , , , , . , , - - - , .
. , erlang — , , . — , , ( ).
Tyyppi
Code.Typespec, . : . , , , . , , . — Tyyppi.of?/2, , — «»/«» , .
iex|tyyppi|1 Tyyppi.of? GenServer.on_start(), {:ok, self()}
#⇒ true
iex|tyyppi|2 Tyyppi.of? GenServer.on_start(), :ok
#⇒ false
- , Tyyppi.T. Tyyppi.of?/2 - — Tyyppi.of_type?/2.
iex|tyyppi|3 type = Tyyppi.parse(GenServer.on_start)
iex|tyyppi|4 Tyyppi.of_type? type, {:ok, self()}
#⇒ true
, , , , , . :erlang.term_to_binary/1, Config.Provider.
, : . , . , key: type(). Access, upserts. , Ecto.Changeset cast_field/1 validate/1.
, , , , ( , ).
defmodule MyStruct do
import Kernel, except: [defstruct: 1]
import Tyyppi.Struct, only: [defstruct: 1]
@typedoc "The user type defined before `defstruct/1` declaration"
@type my_type :: :ok | {:error, term()}
@defaults foo: :default,
bar: :erlang.list_to_pid('<0.0.0>'),
baz: {:error, :reason}
defstruct foo: atom(), bar: GenServer.on_start(), baz: my_type()
def cast_foo(atom) when is_atom(atom), do: atom
def cast_foo(binary) when is_binary(binary),
do: String.to_atom(binary)
def validate(%{foo: :default} = my_struct), do: {:ok, my_struct}
def validate(%{foo: foo} = my_struct), do: {:error, {:foo, foo}
end
Não tenho ideia de qual é o valor prático desta biblioteca na produção (mentira, eu sei: nenhum), mas certamente pode ser um grande auxiliar durante o desenvolvimento, permitindo que você restrinja sua pesquisa e isole erros estranhos associados à natureza dinâmica dos tipos no Elixir especialmente ao lidar com fontes externas.
Todo o código da biblioteca está disponível, como sempre, no github .
Digitação em tempo de execução feliz!