我有一个结构:
defmodule Company do
defstruct [:id, :name, :active]
end
我有一个函数,要求其参数之一属于此结构:
def create(connection, %Company{id: id} = company) do
# stuff
end
有什么方法可以在不使用保护子句的情况下强制执行类型检查?现在我必须这样做:
def create(connection, %Company{id: id, name: name, active: active})
when is_integer(id) and is_binary(name) and is_boolean(active) do
# stuff
end
编辑:这个问题具体与使用struct
的函数定义中的参数有关。
答案 0 :(得分:2)
否,没有保护措施就无法强制执行类型检查。 Erlang(以及Elixir)是一种动态类型化的语言,您可能希望为传递的不同类型使用不同的子句,例如如果类型不符合,或者只是将输入下沉,则返回错误消息。
def create(connection, %Company{id: id})
when not is_integer(id) do
raise "Must be integer"
end
埃尔朗(Erlang)并没有冒着以您需要的方式处理输入错误的自由,这就是为什么您使用警卫的方法。
有一个静态分析工具dialyzer
,您可以使用它来静态检查类型,它也不会阻止编译器和运行时传递任何传递的类型。
答案 1 :(得分:0)
如@Aleksei所指出的那样,由于Elixir是一种动态类型的语言,因此在Elixir中不强制执行类型检查,因此要手动执行此操作,通常会在guard子句中进行。
但是一遍又一遍地重复相同的子句集可能会很麻烦,累人并且容易出错。尽管您可以在单独的函数中“验证”它们并使其更简单,但它的性能不如后卫:
defmodule Company do
defstruct [:id, :name, :active]
def create(connection, company) do
with :ok <- validate(company) do
# do something
end
end
defp validate(%Company{id: id, name: name, active: active})
when is_integer(id) and is_binary(name) and is_boolean(active),
do: :ok
defp validate(_term), do: raise "Invalid Company"
end
现在调用Company
的函数将按预期工作,同时会为其他术语带来错误:
Company.create(1, %Company{})
# => ** (RuntimeError) Invalid Company
Company.create(2, %Company{id: 1, name: "hello", active: false})
# => ... (works normally)
如果您的用例更简单,则可以通过defining a custom guard-clause保留性能。
答案 2 :(得分:0)
在Elixir中,我认为将结构的内容视为有效数据更为习惯。并提供工厂函数来创建结构作为公共API的一部分,如@Sheharyar上面建议的那样。我通常会看到该函数的名称为new
而不是create
,但这比教条更重要。
只要您的代码坚持使用<module>.new
函数来创建结构,就不需要在每个函数上添加重复保护。如果所有操作都出错,请使其崩溃。