当我开始写注解“在不希望有它们的类型”时,在我看来,我已经熟练地将Erlang类型带入运行时,现在我可以在长生不老药的客户端代码中使用它们。哈哈,我真是天真。
链接建议的任何内容都适用于明确的使用地点类型定义,例如use Foo, var: type()
。不幸的是,如果我们想在其他地方定义类型,这种方法注定要失败:在代码旁边使用模块属性,或者在配置中。例如,要定义结构,我们可能要编写如下代码:
# @fields [foo: 42]
# defstruct @fields
@definition var: atom()
use Foo, @definition
上面的代码不是不会按照我们想要的方式处理类型-它根本不会收集,因为它会@definition var: atom()
抛出异常** (CompileError) undefined function atom/0
。
天真的方法
— « » ( @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
我不知道该库在生产中的实际价值(是的,我知道:没有),但是在开发过程中无疑可以提供很大的帮助,使您可以缩小搜索范围并隔离与Elixir中类型的动态性质相关的奇怪错误。特别是在处理外部资源时。
与往常一样,所有库代码在github上都可用。
快乐的运行时输入!