Эликсир: Как избежать глубоко вложенных падежных выражений? - PullRequest
0 голосов
/ 13 июня 2018

Допустим, у меня есть функция main_function, которая зависит от результата трех других функций, каждая из которых может либо возвращать {:ok, result}, либо {:error, error}.Как мне избежать использования глубоко вложенных операторов case, которые в javascript выглядят как ад обратного вызова.

Пример:

def func1(input) do
  case SOMECONDITION do
    {:ok, result} ->
      {:ok, result}
    {:error, error} ->
      {:error, error}
  end
end

def func2(input) do
  case SOMECONDITION do
    {:ok, result} ->
      {:ok, result}
    {:error, error} ->
      {:error, error}
  end
end

def func3(input) do
  case SOMECONDITION do
    {:ok, result} ->
      {:ok, result}
    {:error, error} ->
      {:error, error}
  end
end

def main_function(input) do
  case func1(input) do
    {:ok, result} ->
      case func2(result) do
        {:ok, result} ->
          case func3(result) do
            {:ok, result} ->
              {:ok, EXPECTED_OUTCOME}
            {:error, error} ->
              {:error, error}
          end
        {:error, error} ->
          {:error, error}
      end
      {:error, error} ->
        {:error, error}  
    end
  end
end

Это просто нехорошо ...

Ответы [ 2 ]

0 голосов
/ 13 июня 2018

Отвечая на комментарий к принятому ответу:

Это интересный подход.Но скажем, func1 и func2 пишут в БД.Если func1 возвращает ошибку, я не хочу, чтобы func2 вообще выполнялся.Будет ли это так?

Существует также другой вариант, если вы используете Ecto .

Допустим, func1, func2 иfunc3 все записывают в БД, но шаги не выполняются на func2, что означает, что func1 уже записал в БД.

И, допустим, они возвращают {:ok, result}или {:error, error}.

Ecto.Multi на помощь!

alias Ecto.Multi
alias MyApp.Repo

def main_function(input) do
  result =
    Multi.new()
    |> Multi.run(:result1, fn _ -> func1(input) end)
    |> Multi.run(:result2, &func2(&1.result1))
    |> Multi.run(:result3, &func3(&1.result2))
    |> Repo.transaction()

  case result do
    {:ok, %{result1: _, result2: _, result3: _}} ->
      {:ok, EXPECTED_OUTCOME}

    {:error, error, _changes_so_far} ->
      {:error, error}
  end
end

При использовании Multi возврат кортежа ошибки прервет любые дальнейшие операции и сделает весь мультипотерпеть поражение.Кроме того, поскольку он использует транзакцию, все успешные операции будут откатываться.

0 голосов
/ 13 июня 2018

[Редактировать]: я добавил удобную ссылку на отличный доклад, который охватывает эту концепцию, а также решения для более сложных задач из ElixirConf 2018 ниже.

Не делайтене беспокойся - Эликсир тебя прикрыл.Вы хотите, чтобы эта специальная форма : with/1

with/1 продолжала выполнять функции тогда и только тогда, когда они соответствуют ожидаемому результату.

Ваша основная функция выглядит примерно так:

def main_function(input) do
  with {:ok, result_1} <- func1(input),
       {:ok, result_2} <- func2(result_1),
        ...,
  do: {:ok, EXPECTED_OUTCOME}
end

Когда не удается найти совпадение, скажем, потому что есть кортеж типа {:error, _error}, специальная форма вернет первыйобнаружена ошибка и прекращено выполнение функций.

Вы также можете добавить условие else.В качестве примера, где я использовал это, пользователь может ожидать какое-то действие, требующее выполнения большого количества функций, и я хочу предупредить его об одном и том же, независимо от того, где оно не выполнено:

def main_function(input) do
  with {:ok, result_1} <- func1(input),
       {:ok, result_2} <- func2(result_1),
        ... do
    {:ok, EXPECTED_OUTCOME}
  else
    _error ->
      {:error, "Couldn't complete action"}
  end
end

Дополнительные ресурсы:

Вот удивительный доклад автора Credo об этой концепции, любезно предоставленный ElixirConf 2018: https://www.youtube.com/watch?v=ycpNi701aCs&t=473s

Вот отличный пост на with/1: https://www.erlang -solutions.com / блог / изучения-с-эликсир-специальный form.html

...