Antiforms

An antiform is a variant of a value that can’t be put in a list. It carries the same payload as the ordinary value, but it exists to be a signal rather than data: “nothing here”, “splice these”, “this failed”, “here are several results”. Because an antiform can’t end up inside a block by accident, code that doesn’t know about the signal can’t mistake it for data.

Eight fundamental types have antiforms, each with its own meaning:

Antiform Of What it signals
LOGIC! WORD! ~okay~ and ~null~: true, and the one false
SPLICE! BLOCK! items to be spliced into a list, not added as one
DATATYPE! FENCE! the type of a value
ACTION! FRAME! a function that runs when its word is evaluated
VOID! BLANK! no value
TRASH! TAG! an unusable value, with a note saying why
PACK! GROUP! several results at once
FAILURE! ERROR! an error that must be dealt with

How They Look

The console shows an antiform between backslashes, with a comment giving its type. The notation inside the backslashes is the antiform’s quasiform: the same payload with tildes around it.

>> spread [a b]
== \~[a b]~\  ; antiform (splice!)

>> null
== \~null~\  ; antiform (logic!)

>> type of 1
== \~{integer!}~\  ; antiform (datatype!)

Quasiforms, LIFT, and UNLIFT

An antiform can’t go in a list, but its quasiform can. Evaluating a quasiform gives the antiform back:

>> first [~[a b]~]
== ~[a b]~

>> ~[a b]~
== \~[a b]~\  ; antiform (splice!)

LIFT turns any value into something that can be stored: antiforms become quasiforms, and everything else gains a level of quoting. UNLIFT undoes it exactly. So a signal can be put into “suspended animation” in a list, and brought back later:

>> lift spread [a b]
== ~[a b]~

>> lift 1
== '1

>> unlift first [~[a b]~]
== \~[a b]~\  ; antiform (splice!)

>> unlift first ['1]
== 1

This one pair of operations works for every antiform, current and future, which is the reason antiforms are a general mechanism rather than a collection of special types.

Stable and Unstable

Antiforms come in two grades.

Stable antiforms (LOGIC!, SPLICE!, DATATYPE!, ACTION!) can be held in a variable and passed as an argument. They just can’t be put in a list.

>> s: spread [a b]
== \~[a b]~\  ; antiform (splice!)

>> append [x] s
== [x a b]

>> reduce [null]
** PANIC: Invalid use of ~null~ antiform

Unstable antiforms (VOID!, TRASH!, PACK!, FAILURE!) can’t even be held in a variable. Assigning one either decays it to a stable value (a PACK! becomes its first item), unsets the variable (VOID!), or errors (FAILURE!). They have to be dealt with where they are produced, or LIFTed first.

>> p: pack [1 2]
== \~('1 '2)~\  ; antiform (pack!)

>> p
== 1

>> x: fail "oops"
** Failure: oops

The practical consequence: anything that can sit in a variable can be tested by IF without special handling, because the antiforms that can’t be tested are exactly the ones that can’t sit in a variable.

Telling Them Apart

>> antiform? spread [a]
== \~okay~\  ; antiform (logic!)

>> element? spread [a]
== \~null~\  ; antiform (logic!)

>> element? 1
== \~okay~\  ; antiform (logic!)

>> any-stable? spread [a]
== \~okay~\  ; antiform (logic!)

>> quasiform? first [~[a]~]
== \~okay~\  ; antiform (logic!)

>> type of first [~[a]~]
== \~{quasiform!}~\  ; antiform (datatype!)

To ask which fundamental type an antiform came from, lift it, take the quasi off, and ask that:

>> type of unquasi lift spread [a]
== \~{block!}~\  ; antiform (datatype!)

>> type of unquasi lift null
== \~{word!}~\  ; antiform (datatype!)

Why

The design argument, and the history of how the eight came to be the eight, is on the forum rather than here:

  • A Justification of Generalized Isotopes (2022, kept current) – why one lift/unlift mechanism beats a pile of special types, starting from the old APPEND/ONLY problem. (“Isotopes” was the earlier name for antiforms and quasiforms together.)
  • Which Antiforms Are In Use? (2024, kept current) – the table above, and a history of which type each antiform has been attached to over time.

Table of contents

  • LOGIC! - either ~okay~, or ~null~ (the only "falsey" state)
  • SPLICE! - fragment of multiple values without a surrounding container
  • DATATYPE! - the type of a value expressed as an antiform
  • ACTION! - will trigger function execution from words
  • VOID! - panics on WORD! access, multi-step evaluations discard
  • TRASH! - panics on WORD! access, console does not display
  • PACK! - multi-return that decays to first item, empty is HEAVY VOID
  • FAILURE! - failure state that is escalated to a panic if not triaged