Skip to main content
Polar supports several operators used to construct rule bodies. These operators define how expressions are evaluated during rule execution.

Unification (=)

Unification is Polar’s core matching operation. Two values unify if they are equal or if variables can be consistently bound to make them equal. Lists unify if all corresponding elements unify. The unification operator (=) checks if its left and right operands unify; for example, "a" = "a", x = "a", or ["a", "b"] = [x, "b"] where the variable x is either bound to "a" or unbound. Note that in the case of String values, unification is case sensitive, e.g.:
  • "x" does not unify with "X"
  • User{"Alice"} does not unify with User{"alice"}

Conjunction (and)

and requires both conditions in a rule body to be true.

Disjunction (or)

or succeeds if either the left or right operand is true. Any or expression can be rewritten as multiple rules with the same head but different bodies.

Negation (not)

not checks that a specific fact does not exist. This would look like below: For example, you might use it to say that a user should only be allowed to perform an action if they are not banned, and that the policy grants them that permission:
Negation rules:
  • You can negate only a single fact, not a compound expression (like one using and or or), or a fact that refers to another policy rule.
  • Variables inside a negated fact must also appear in a non-negated fact in the same rule. This ensures Polar knows the set of possible values before evaluating the negation.
These restrictions allow Polar to compile negation into efficient NOT EXISTS SQL subqueries. Polar supports a wide range of patterns using negation, including common use cases like:
  • Checking that a user meets all required conditions (e.g., has every required role)
  • Allowing access from a parent resource unless explicitly overridden

List Membership (in)

in iterates over a list of strings.
x is unified with each list element in sequence. The right operand must be a list of strings. In the following example, the variable x will be bound to "a", "b", and "c", in turn, and then the x = "a" check will evaluate. This expression will only succeed for the first item in the list, "a".
x is unified with each list element in sequence. The right operand must be a list of strings. This succeeds only for the first element, "a". The left operand can be a value instead of a variable:
This succeeds twice, once for the first element and once for the fourth.

Integer Comparisons

<, <=, >, and >= compare integer values. If a fact stores a Unix timestamp:
You can write:

Inequality (!=)

The inequality operator (!=) checks that two values are not equal.
This rule grants edit permission to editors, but only when they are not the owner of the resource.
The != operator is not supported in local authorization. Use centralized authorization for policies that require inequality comparisons.

matches Operator

matches asserts that a variable is of a specific type.
Typically used when introducing a new variable, such as when joining two rules.
This binds group to a Group type and requires both has_group and has_role to succeed with the same Group value.

Operator Precedence

Polar operators are evaluated from highest precedence to lowest as follows:
  1. in, matches
  2. =, <, <=, >, >=
  3. not
  4. and
  5. or
For example, the following assertion passes:
Oso evaluates the conditions as follows:
That is, b(user) and c(user) is evaluated first. It returns false, but because a(user) is true, the entire condition evaluates to true or false or false, which returns true. Use () to explicitly control evaluation order. For instance, in the example above, if you instead wanted to state that e is true only if at least one of a and b is true, and at least one of c and d is true, you could rewrite the rule as follows:
This evaluates the or expressions before applying and.