Избегайте указания защитного предложения при использовании структуры

У меня есть структура:

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.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
364
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Нет, без защиты невозможно принудительно выполнить проверку типа. 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 для создания структур, вам не нужно добавлять повторяющиеся защиты для каждой функции. Если все пойдет не так, пусть рухнет.

Другие вопросы по теме