Biconditional (<==>)
The biconditional operator expresses logical equivalence: "A if and only if B". Like ==>, it is a proof/specification operator rather than a general runtime boolean operator.
Syntax
condition1 <==> condition2
The expression a <==> b is true when both sides have the same truth value:
- Both
aandbare true - Both
aandbare false
It is logically equivalent to (a ==> b) and (b ==> a), or equivalently (a and b) or (not a and not b).
<==> is accepted in ghost contexts such as contracts, static assert, loop:invariant, pure functions, and lemmas. Runtime code should use a == b for boolean equality.
Using Biconditional in Assertions
The biconditional is useful for verifying that two expressions are logically equivalent:
persistent actor IffOperator {
public func equalZero(x : Int) : async Bool
ensures result == (x == 0);
{
assert (x == 0 <==> (x <= 0 and x >= 0));
x == 0
};
}
This assertion proves that x == 0 is equivalent to x being both less-than-or-equal-to zero and greater-than-or-equal-to zero.
Using Biconditional in Contracts
Use <==> in postconditions to express that two conditions are equivalent:
public func isEmpty(arr : [Nat]) : async Bool
ensures result <==> arr.size() == 0;
{
arr.size() == 0
};
This contract states that the return value is true exactly when the array is empty.
Common Patterns
Verifying Logical Laws
The biconditional is ideal for verifying logical equivalences like the contrapositive:
public func contrapositive(p : Bool, q : Bool) : Bool
ensures result == (not p or q);
ensures (p ==> q) <==> (not q ==> not p); // Contrapositive law
{
not p or q
};
De Morgan's Laws
Verify that boolean transformations preserve meaning:
public func deMorgan1(a : Bool, b : Bool) : Bool
ensures result == not (a and b);
ensures result <==> (not a or not b); // De Morgan
{
not (a and b)
};
Characterizing Return Values
Express the exact condition under which a function returns true:
public func isPositive(x : Int) : async Bool
ensures result <==> x > 0;
{
x > 0
};
Relating Runtime and Specification
Use biconditional to connect runtime boolean expressions with their specification forms:
public func biImplicationLogic(x : Bool, y : Bool) : Bool
ensures result == ((not x or y) and (not y or x)); // Runtime form
ensures (x <==> y) ==> result; // Specification form
{
(not x or y) and (not y or x)
};
Precedence
The <==> operator has the same precedence as ==>, which is lower than and and or:
a and b <==> c and d // Parsed as: (a and b) <==> (c and d)
Use parentheses for clarity when mixing with implication:
(a <==> b) ==> c // If a and b are equivalent, then c
a <==> (b ==> c) // a is equivalent to (b implies c)
The operator is non-associative:
// This is rejected:
// a <==> b <==> c
// Use explicit grouping:
(a <==> b) and (b <==> c) // a, b, and c are all equivalent
Truth Table
a | b | a <==> b |
|---|---|---|
| false | false | true |
| false | true | false |
| true | false | false |
| true | true | true |
Comparison with Equality
For boolean values, a <==> b and a == b produce the same result. However, <==> is preferred in specifications because:
- It emphasizes logical equivalence rather than value comparison
- It reads naturally: "a if and only if b"
- It signals intent to verify a logical relationship
Summary
a <==> bmeans "a if and only if b" (logical equivalence)- True when both sides have the same truth value
- Use to verify logical laws, characterize return values, and express equivalences
- Only available in ghost/specification contexts
- Same precedence as
==>, lower thanand/or, non-associative
Related Topics
- Implication for one-way conditional guarantees
- Combining conditions for precedence and grouping
- Pure functions in contracts for naming boolean predicates
- Contract subtyping for implication-style reasoning between caller and callee contracts