У меня есть структура:
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.





Нет, без защиты невозможно принудительно выполнить проверку типа. Erlang (и, следовательно, Elixir) - это язык с динамической типизацией, и вам может потребоваться передать разные предложения для разных типов, например сообщение об ошибке, если типы не соответствуют, или просто поглотить ввод, или что-то еще.
def create(connection, %Company{id: id})
when not is_integer(id) do
raise "Must be integer"
end
Erlang не оставляет за собой право иметь дело с неправильным вводом способом тебе нужно, поэтому вам следует использовать средства защиты.
Существует инструмент статического анализа dialyzer, который вы можете использовать для статической проверки типов, также он не мешает компилятору и среде выполнения передавать любой переданный тип.
Как уже отмечал @Aleksei, проверка типов не применяется в Elixir, поскольку это язык с динамической типизацией, поэтому, чтобы сделать это вручную, мы обычно делаем это в разделе защиты.
Но повторение одного и того же набора предложений снова и снова может быть беспорядочным, утомительным и подверженным ошибкам. Хоть вы можете «проверить» их в отдельной функции и упростит, но он не будет таким производительным, как охранники:
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)
If your use-case was simpler, you could've retained the performance by defining a custom guard-clause.
Я думаю, что в Elixir более идиоматично рассматривать содержимое структуры как допустимые данные. И предоставить фабричную функцию для создания структуры как части вашего общедоступного API, как предлагает @Sheharyar выше. Обычно я вижу, что функция называется new, а не create, но это скорее предпочтение, чем догма.
Пока ваш код использует функцию <module>.new для создания структур, вам не нужно добавлять повторяющиеся защиты для каждой функции. Если все пойдет не так, пусть рухнет.
Возможный дубликат Как можно проверить / применить типы и значения для структур Elixir?