Skip to content

Booleans

Bill Hails edited this page Feb 19, 2026 · 27 revisions
true and false;

is false. You receive similarly sensible results for or, xor and not, as well as the more esoteric nand, nor and xnor, and comparison operators return boolean values:

10 < 12 and 12 < 14; // true

Evaluation of the binary boolean operators is short circuited appropriately so true or something will not evaluate something. The exceptions are xor and xnor which must evaluate both their arguments.

Comparison Operators

The standard comparison operators are available. They require that the types of their arguments agree, and in F♮ any two data with the same type can be compared (sometimes somewhat arbitrarily, for example false < true.) The operators are:

op name
== equal to
!= not equal to
< less than
> greater than
<= less than or equal to
>= greater than or equal to

Some effort has been made to allow all data to be sortable:

  • When comparing user defined types they sort in the order they are declared in, for example after typedef color { red | green | blue }, red < green and green < blue.
  • Functions sort (provided they have the same type signature) by their bytecode address.
  • Complex numbers do not have a well defined sort order in certain edge cases so I've imposed a fairly arbitrary "tie breaking" mechanism, discussed below.

"Spaceship" Operator

Other than the true boolean comparison operators, there's also a "spaceship" binary comparison operator <=>, borrowed from Perl, which returns one of three values: lt, eq or gt. This is obviously more efficient, especially when comparing deeply nested structures, and works well with a switch statement:

switch (a <=> b) {
  (lt) { do stuff if a < b }
  (eq) { do stuf if a == b }
  (gt) { do stuff if a > b }
}

The spaceship operator implementation as you might expect is actually used by all the other comparison operators. As mentioned above this is fairly straightforward for most types, but complex numbers are points on a plane and have no well-defined linear sequence. To allow deterministic sorting anyway, the implementation uses a geometric tie-break strategy.

For a complex number $z = x + yi$, define the sort key:

$$ K(z) = (x + y,; x) $$

and compare keys lexicographically.

Equivalent pseudocode:

let ax = com_real(a);
let ay = com_imag(a) / 1i;
let bx = com_real(b);
let by = com_imag(b) / 1i;

switch ((ax + ay) <=> (bx + by)) {
  (lt) { lt }
  (gt) { gt }
  (eq) {
    switch (ax <=> bx) {
      (lt) { lt }
      (eq) { eq }
      (gt) { gt }
    }
  }
}

This is equivalent to the current implementation:

switch(com_real(a) <=> com_real(b), com_imag(a) / 1i <=> com_imag(b) / 1i) {
    (lt, lt) { lt }
    (lt, eq) { lt }
    (lt, gt) { compare_magnitudes(a, b) }
    (eq, lt) { lt }
    (eq, eq) { eq }
    (eq, gt) { gt }
    (gt, lt) { compare_magnitudes(a, b) }
    (gt, eq) { gt }
    (gt, gt) { gt }
}

where compare_magnitudes is

switch(com_real(a) + com_imag(a) / 1i <=> com_real(b) + com_imag(b) / 1i) {
    (lt) { lt }
    (eq) { if (com_real(a) < com_real(b)) { lt } else { gt } }
    (gt) { gt }
}

A picture should help:

complex comparison

Intuition: lines of constant $x+y$ have slope $-1$. First compare which line each point is on. If both points are on the same line, compare $x$ to break the tie. The tie-break axis is arbitrary, but fixed, so the ordering is deterministic and consistent.

Possible vector adaptation (proposal only)

This is not part of the current language, but the same pattern can be reused later if vectors are introduced.

For $v=(v_1,\dots,v_n)$, a direct generalization is to compare by:

$$ K(v) = \left(\sum_i v_i,; v_1,; v_2,; \dots,; v_n\right) $$

lexicographically (after validating equal dimensions).

That keeps the same design principle:

  1. compare by a projection (the sum),
  2. break ties by a fixed coordinate order.

Whether vectors should be in the numeric tower or a separate type family is still an open design choice.

Next: Conditionals

Clone this wiki locally