当前位置: 首页>>代码示例 >>用法及示例精选 >>正文


Elixir Kernel.SpecialForms.with用法及代码示例


Elixir语言中 Kernel.SpecialForms.with 相关用法介绍如下。

用法:

with(args)
(宏)

用于组合匹配子句。

让我们从一个例子开始:

iex> opts = %{width: 10, height: 15}
iex> with {:ok, width} <- Map.fetch(opts, :width),
...>      {:ok, height} <- Map.fetch(opts, :height) do
...>   {:ok, width * height}
...> end
{:ok, 150}

如果所有子句都匹配,则执行 do 块,并返回其结果。否则,链中止并返回不匹配的值:

iex> opts = %{width: 10}
iex> with {:ok, width} <- Map.fetch(opts, :width),
...>      {:ok, height} <- Map.fetch(opts, :height) do
...>   {:ok, width * height}
...> end
:error

守卫也可以在模式中使用:

iex> users = %{"melany" => "guest", "bob" => :admin}
iex> with {:ok, role} when not is_binary(role) <- Map.fetch(users, "bob") do
...>   {:ok, to_string(role)}
...> end
{:ok, "admin"}

for/1 一样,绑定在 with/1 内的变量不会泄漏。没有<- 的表达式也可以用在子句中。例如,您可以使用 = 运算符执行常规匹配:

iex> width = nil
iex> opts = %{width: 10, height: 15}
iex> with {:ok, width} <- Map.fetch(opts, :width),
...>      double_width = width * 2,
...>      {:ok, height} <- Map.fetch(opts, :height) do
...>   {:ok, double_width * height}
...> end
{:ok, 300}
iex> width
nil

子句中任何表达式的行为都与在 with 之外编写的行为相同。例如,= 将引发 MatchError 而不是返回不匹配的值:

with :foo = :bar, do: :ok
** (MatchError) no match of right hand side value: :bar

与 Elixir 中的任何其他函数或宏调用一样,也可以在 do - end 块之前的参数周围使用显式括号:

iex> opts = %{width: 10, height: 15}
iex> with(
...>   {:ok, width} <- Map.fetch(opts, :width),
...>   {:ok, height} <- Map.fetch(opts, :height)
...> ) do
...>   {:ok, width * height}
...> end
{:ok, 150}

parens 和 no parens 之间的选择是一个偏好问题。

其他条款

在匹配失败的情况下,可以给出 else 选项来修改从 with 返回的内容:

iex> opts = %{width: 10}
iex> with {:ok, width} <- Map.fetch(opts, :width),
...>      {:ok, height} <- Map.fetch(opts, :height) do
...>   {:ok, width * height}
...> else
...>   :error ->
...>     {:error, :wrong_data}
...>
...>   _other_error ->
...>     :unexpected_error
...> end
{:error, :wrong_data}

else 块的工作方式类似于case 子句:它可以有多个子句,并且将使用第一个匹配项。 with 内绑定的变量(例如本例中的 width)在 else 块中不可用。

如果使用 else 块并且没有匹配的子句,则会引发 WithClauseError 异常。

谨防!

请记住,with 的潜在缺点之一是所有失败子句都被扁平化为单个 else 块。例如,使用以下代码检查给定路径是否指向 Elixir 文件,并且在创建备份副本之前它是否存在:

with ".ex" <- Path.extname(path),
     true <- File.exists?(path) do
  backup_path = path <> ".backup"
  File.cp!(path, backup_path)
  {:ok, backup_path}
else
  binary when is_binary(binary) ->
    {:error, :invalid_extension}

  false ->
    {:error, :missing_file}
end

请注意,我们必须如何重构 Path.extname/1 File.exists?/1 的结果类型来构建错误消息。在这种情况下,最好将 with 子句更改为已经返回所需的格式,如下所示:

with :ok <- validate_extension(path),
     :ok <- validate_exists(path) do
  backup_path = path <> ".backup"
  File.cp!(path, backup_path)
  {:ok, backup_path}
end

defp validate_extname(path) do
  if Path.extname(path) == ".ex", do: :ok, else: {:error, :invalid_extension}
end

defp validate_exists(path) do
  if File.exists?(path), do: :ok, else: {:error, :missing_file}
end

请注意,一旦我们确保 with 中的每个子句都返回规范化格式,上面的代码是如何组织得更好和更清晰的。

相关用法


注:本文由纯净天空筛选整理自elixir-lang.org大神的英文原创作品 Kernel.SpecialForms.with(args)。非经特殊声明,原始代码版权归原作者所有,本译文未经允许或授权,请勿转载或复制。