测试守卫的真实性

Testing truthiness in guards

我可以使用守卫来测试参数是否为 true:

defmodule Truth do
  def true?(term) when term, do: "#{term} is true"
  def true?(term), do: "#{term} is not true"
end

这对于布尔值按预期工作:

Truth.true?(true)
#=> "true is true"
Truth.true?(false)
#=> "false is not true"

但无法验证其真实性:

Truth.true?(1)
#=> "1 is not true"

是否可以测试守卫的真实性?比如下面的函数可以用上面true?/1的风格使用guards来写吗?

def truthy?(term) do
  if term, do: "#{term} is truthy", else: "#{term} is falsey"
end

根据 official documentation for Guards:

Guards start with the when keyword, which is followed by a boolean expression.

所以guards中的表达式必须是布尔表达式

长生不老药的真实性由 if/2 等宏定义。这些宏在守卫内部不可用,因此我们需要另一种方法将术语转换为布尔值。

if/2的文档(和implementation)可以看出,真实的定义是falsenil都是假的,其他都是真.所以我们可以用它来实现真实性守卫:

defmodule Truth do
  defguard is_falsey(term) when term in [false, nil]
  defguard is_truthy(term) when not is_falsey(term)

  def truthy?(foo) when is_truthy(foo), do: "#{foo} is truthy"
  def truthy?(foo), do: "#{foo} is falsey"
end

这会按预期工作:

Truth.truthy?(true)
#=> "true is truthy"
Truth.truthy?(1)
#=> "1 is truthy"
Truth.truthy?(false)
#=> "false is falsey"
Truth.truthy?(nil)
#=> " is falsey"