使用结构时避免指定保护子句

时间:2018-11-25 09:48:42

标签: elixir

我有一个结构:

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的函数定义中的参数有关。

3 个答案:

答案 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函数来创建结构,就不需要在每个函数上添加重复保护。如果所有操作都出错,请使其崩溃。