# Expressions and operators

| Precedence | Operators | Operands | Result |
|---|---|---|---|
| 1 (tightest) | `not`, unary `-` | `Bool`; a number | same |
| 2 | `*`, `/`, `%` | `*` and `%` may mix named types; `/` needs the same named type: `Int` drops the remainder, `Decimal` is exact | `Int`, or `Decimal` if either side is `Decimal` |
| 3 | `+`, `-` | the same named type after widening, or a literal; `+` also joins two `Text` values | as above |
| 4 | `<`, `<=`, `>`, `>=` | two numbers or two time values of the same named type; text has no order | `Bool` |
| 5 | `==`, `!=` | two values of the same type after widening, or a nullable value and `none` | `Bool` |
| 6 | `and` | `Bool` | `Bool` |
| 7 (loosest) | `or` | `Bool` | `Bool` |

"Widening" is one type standing in for another it is built on
([When one type can stand in for another](when-one-type-can-stand-in-for-another.md)).

Adding two values needs the same type on each side, and multiplying may
mix them, because multiplying a price by a quantity means something and
adding them doesn't:

```jadpo
var wrong: Stock = Stock(per_pack + packs)
var line: Stock = Stock(per_pack * packs)
var earlier: Bool = name < other_name
var share: ItemPrice = decimal.divide(total, people, rounding: Rounding.half_even)
var boxes: BoxCount = integer.divide(items, box_size, rounding: Rounding.up)
```

- `per_pack + packs` is an error: it adds a `Quantity` to a `Stock`.
- `per_pack * packs` is fine: it multiplies different units.
- `name < other_name` is an error: text has no order, because the order
  depends on the language. Sort text with `list.sort_by` and a text key.
- `decimal.divide` and `integer.divide` divide values of different types,
  or round another way, with the rounding you name.
- `box_size`'s type excludes zero. A divisor's type must, so dividing by
  zero is ruled out while compiling
  ([`type.divisor_zero`](../errors/type.divisor_zero.md)).

In `+`, `-` and `/`, a literal takes the other side's type, exactly as it
does in `==`. So `price + 3` with a `Decimal` price is an error, as
`price == 3` is: write `3.00`. `+` joins two `Text` values only when they
are the same named type after widening, or one is a literal, as `==`
compares them. `*` and `%` still mix.

Operators work on the built-in type a named type is built on, so the
result is a plain `Int`, `Decimal`, `Text` or `Bool`. To turn it back into
a named type, construct it: `Stock(total + holdback)`
([Creating values](creating-values.md)). Brackets
group. `and` and `or` stop as soon as the answer is known. `a < b < c` is
an error, because `a < b` is a `Bool`.

Logical operators are words. `!`, `&&` and `||` are not Jadpo, and each is
one error whose edit writes the word (`syntax.logical_operator`). Nor are
`===` and `!==`: `==` and `!=` already compare values of one type, so each
is one error whose edit writes `==` or `!=` (`syntax.equality_operator`).
So are SQL's `<>`, `=` and `IS NULL`
([Habits from other languages](logic.md#habits-from-other-languages)).

Operators on standard types, such as `price * 20%` or `distance / 2h`,
follow [the standard operator table](the-standard-operator-table.md).
Anything in neither that table nor the one above is a compile error naming
the two types. `20%` is a `Percentage` literal. The remainder operator
keeps `%` with spaces round it, as the formatter writes every binary
operator, and `20%3` is `syntax.percent_ambiguous`, naming both readings.
`Rounding.down` rounds toward zero ([Numbers](numbers.md)).
