Clinker user guide · ← back to Null Handling

Where does
a null go?

A null is a value that isn't there: an empty cell, a missing field. Most things you do with a null give null back, and a filter treats null as "no". Set a record's fields to a value or to null, pick an expression, and see every step of how it's worked out.

01

Kept or dropped?A filter keeps a record only when its condition is exactly true

A filter condition has three possible results: true, false or null. Only true keeps the record. Null is treated like false, so a record whose condition comes out null is dropped too.

That catches people out with "not". If amount is null, then amount > 100 is null, and so is not (amount > 100). Both filters drop the record, so a record can fail a test and its opposite at the same time.

== and != are different. They never give null: null == null is true, and null != 100 is true. So filter amount != 100 keeps a record with no amount. Every other comparison (<, >, <=, >=) and every calculation gives null when either side is null.

To decide yourself what happens to empty values, ask for them directly with is_null(), or give a default first with ??: filter (amount ?? 0) > 100.

02

ValuesWhat null does inside if, match, ??, sums and methods

  • if … then … else takes the else branch when the condition is null. With no else, the result is null.
  • match without a subject skips an arm whose condition is null. With a subject (match status { … }) it compares with ==, so a null => arm matches a missing value.
  • ?? gives the right side only when the left side is null.
  • Calculations (+ - * /) give null if either side is null.
  • Methods on a null give null without running, except is_null(), is_empty(), type_of() and catch(x), which are made for nulls.
The record panel is shared with section 01: change a field there and these results follow.
03

and · or · notA null decides nothing unless the other side settles it

Read null as "unknown". false and unknown is false whatever the unknown is, so it's false. true and unknown could go either way, so it stays null. The same reasoning makes true or null true. The engine also stops early: if the left side of and is false, or the left side of or is true, the right side is never worked out. The tree in section 01 greys it out.

04

TrapsEach of these runs without an error and gives a surprising answer

A test and its opposite both fail

For a record with no amount, filter amount > 100 and filter not (amount > 100) both drop it. Splitting records into two outputs with a condition and its opposite loses the empty ones. Send them somewhere on purpose with is_null().

!= keeps empty values

filter status != "closed" keeps records with no status, because null != "closed" is true. Add and not status.is_null() if they should go.

null == null is true

Two missing values count as equal. Comparing two empty fields with == gives true, not unknown. Some other tools treat this differently, so don't carry that habit over.

One empty field empties the total

price * qty is null if either is missing, and so is anything built on it. Give defaults where the value enters: (qty ?? 1).

debug shows nothing for null

.debug("label") logs the value it's called on, except null: on a null it returns null without logging, like most methods. Check with is_null() instead.