保护条款的基本用法

在 Elixir 中,可以创建具有相同名称的函数的多个实现,并指定在调用函数之前将应用于函数的参数的规则,以便确定要运行的实现。

这些规则由关键字 when 标记,它们位于函数定义中的 def function_name(params)do 之间。一个简单的例子:

defmodule Math do

  def is_even(num) when num === 1 do
    false
  end
  def is_even(num) when num === 2 do
    true
  end

  def is_odd(num) when num === 1 do
    true
  end
  def is_odd(num) when num === 2 do
    false
  end

end

假设我用这个例子运行 Math.is_even(2)is_even 有两种实现方式,具有不同的保护条款。系统将按顺序查看它们,并运行参数满足 guard 子句的第一个实现。第一个指定 num === 1 不是真的,所以它移动到下一个。第二个指定 num === 2,这是真的,所以这是使用的实现,返回值将是 true

如果我运行 Math.is_odd(1) 怎么办?系统查看第一个实现,并看到由于 num1,第一个实现的保护条款得到满足。然后它将使用该实现并返回 true,而不是在查看任何其他实现。

警卫在他们可以运行的操作类型方面受到限制。 Elixir 文档列出了每个允许的操作 ; 简而言之,它们允许比较,数学,二元运算,类型检查(例如 is_atom)和一些小的便利功能(例如 length)。可以定义自定义保护子句,但它需要创建宏,最好留给更高级的指南。

请注意,警卫不会抛出错误; 它们被视为保护条款的正常故障,系统继续查看下一个实现。如果你发现在调用带有 params 的保护函数时你得到了什么,那么你期望它可能会起作用,那么你期望工作的一个保护条款就是抛出一个被误吞的错误。

要自己看一下这个,创建然后调用一个没有意义的保护函数,例如这个试图除以零的函数:

defmodule BadMath do
  def `divide(a)` when a / 0 === :foo do
    :bar
  end
end

调用 BadMath.divide("anything") 将提供有点无益的错误 (FunctionClauseError) no function clause matching in BadMath.divide/1 - 而如果你试图直接运行 "anything" / 0,你会得到一个更有用的错误:(ArithmeticError) bad argument in arithmetic expression