如何区分Elixir的`with`宏中的错误?

时间:2019-08-05 13:21:05

标签: elixir

通常,我的代码使用with宏来确保在继续操作之前所有必需的数据都可用,但是我希望有更多细粒度的错误来准确确定其失败原因。

使用文档中的示例:

with {:ok, width} <- Map.fetch(opts, :width),
     {:ok, height} <- Map.fetch(opts, :height) do
  {:ok, width * height}
else
  :error ->
    {:error, :wrong_data}
end

我想知道错误元组中是否缺少widthheight

我试图使用默认值:

with {:ok, width} <- Map.fetch(opts, :width, {:error, :missing_width}),
     {:ok, height} <- Map.fetch(opts, :height, {:error, :missing_height}) do
  {:ok, width * height}
else
  {:error, reason} = error -> error
end

但这并不特别优雅。有没有更惯用的方式?

2 个答案:

答案 0 :(得分:5)

您可以将with行包裹在描述性元组上,并仍然在所需的返回值上断言,这将使您可以识别/提供错误所在的反馈。

with(
     {_, {:ok, width}} <- {:width, Map.fetch(opts, :width)},
     {_, {:ok, height}} <- {:height, Map.fetch(opts, :height)}
  ) do

  {:ok, width * height}

else
  {what, :error} ->
    {:error, {what, :wrong_data}}
end

答案 1 :(得分:0)

这可能与您提供的示例有关,但是您始终可以创建包装器模块/函数。

defmodule MapHelpers do
  def fetch_number(%{} = map, field) do
    case Map.fetch(map, field) do
      {:ok, val} when is_number(val) ->
        {:ok, val}

      {:ok, val} -> 
        {:error, "#{to_string(val)} in attribute #{to_string(field)} is not a number"}

      :error ->
        {:error, "#{to_string(field)} is not defined"}
    end
  end
end

然后,您在其他地方的代码将变得更加简单:

with {:ok, width} <- MapHelpers.fetch_number(opts, :width),
     {:ok, height} <- MapHelpers.fetch_number(opts, :height) do
    {:ok, width * height}
end

通过这种方式,您不必定义else块,并且具有可重用的代码来从地图中提取数字。