Current section

Files

Jump to
bond lib bond predicates.ex
Raw

lib/bond/predicates.ex

defmodule Bond.Predicates do
@moduledoc """
Predicate functions and operators that are useful in assertions and contract specifications.
This module is automatically imported for all assertion expressions, specifically in
preconditions defined with `@pre`, postconditions defined with `@post`, and in uses of
`Bond.check/1`.
To use the infix operator versions of the predicates in other contexts, this module must be
imported in the using module.
"""
@doc """
Logical exclusive or: is either `p` or `q` true, but not both?
For an infix operator version of exclusive or see `|||/2`.
## Examples
iex> xor(true, true)
false
iex> xor(true, false)
true
iex> xor(false, true)
true
iex> xor(false, false)
false
"""
@spec xor(as_boolean(term()), as_boolean(term())) :: boolean()
def xor(p, q), do: (p || q) && !(p && q)
@doc """
Logical exclusive or operator: `p ||| q` means `xor(p, q)`.
Note that the `|||` operator has higher precedence than many other operators and it may be
necessary to parenthesize the expressions on either side of the operator to get the
expected result.
## Examples
iex> true ||| true
false
iex> true ||| false
true
iex> false ||| true
true
iex> false ||| false
false
iex> x = 2
2
iex> y = 4
4
iex> (x - y < 0) ||| (y <= x)
true
"""
def p ||| q, do: xor(p, q)
@doc """
Logical implication: does `p` imply `q`?
For an infix operator version of logical implication see `~>/2`.
## Examples
iex> implies?(true, true)
true
iex> implies?(true, false)
false
iex> implies?(false, true)
true
iex> implies?(false, false)
true
"""
@spec implies?(as_boolean(term()), as_boolean(term())) :: boolean()
def implies?(p, q), do: !!(!p || q)
@doc """
Logical implication operator: `p ~> q` means `implies?(p, q)`.
Note that the `~>` operator has higher precedence than many other operators and it may be
necessary to parenthesize the expressions on either side of the operator to get the
expected result.
## Examples
iex> true ~> true
true
iex> true ~> false
false
iex> false ~> true
true
iex> false ~> false
true
iex> x = 2
2
iex> y = 4
4
iex> (x - y < 0) ~> (y > x)
true
"""
def p ~> q, do: implies?(p, q)
@doc """
Pattern matching operator: equivalent to `match?(pattern, expression)`.
## Examples
iex> {:ok, %Date{}} <~ Date.new(1974, 6, 6)
true
iex> {:error, _} <~ Date.new(-1, -1, -1)
true
"""
defmacro pattern <~ expression do
quote do
case unquote(expression) do
unquote(pattern) -> true
_unmatched -> false
end
end
end
end