Skip to content

2.0.0-rc.23

Pre-release
Pre-release

Choose a tag to compare

@reid-spencer reid-spencer released this 24 Aug 12:42
· 76 commits to main since this release

2.0.0-rc.23

Delta from 2.0.0-rc.22. See the rc.1 notes for the full 2.0 release story.

One language addition, closing a hole in the type system.

empty — writing the value a type already promised

RIDDL could declare T? and T* but could not write their empty inhabitants.
There was no literal for either, so a model could express acquiring a value and never
releasing one — and a whole class of ordinary transition was unsayable:

  • releasing a hold, clearing a cancellation reason
  • un-assigning a driver, a table, a bed
  • emptying a cart, clearing a line-item list on void

In a constructor a field can simply be omitted, but set has no such escape, so these
were being written as prose strings. Reported by riddl-models, where 686 set … to "…"
prose values are being eliminated and these could not be.

set field Prescription.OnHold.holdReason to empty
set field TableOrder.orderItems to none        // `none` is a SYNONYM
let e = empty String(1,30)*                    // ascribed, carries its own type

One literal, and none is a synonym, not a second construct: both spellings produce
the identical AST and prettify converges them to empty. Same choice not/! made — a
spelling flag would let two ASTs meaning the same thing compare unequal.

Legal exactly where the minimum cardinality is zero:

type empty
T?, T*, T{0,n} legal
T+, T{1,n}, bare T Error

That single rule is why one literal serves both the absent optional and the empty
collection — they are the same inhabitant under different upper bounds — and it makes the
check total over the cardinality wrappers rather than special-casing two.

The ascribed form is not sugar. A bare empty takes its type from the position, and
only let, constant and set supply one — notably not a constructor argument, which
is where models most want it (record Data(items = empty)). empty T* is the form that
works everywhere.

Two limitations worth knowing

  • A bare empty is not checked against a field typed INLINE. The expected-type
    machinery resolves only named types, so note: String(1,20)+ gives the check nothing
    to compare against. Declare the type, or use the ascribed form, and it is exact. This
    is pre-existing behaviour shared with typed holes, not new.
  • let e: T* = empty does not parse, because let's ascription is a type reference
    and an inline T* is not one. let e: SomeNamedType = empty works, as does
    let e = empty T*. Also pre-existing.

Arithmetic remains out of scope, deliberately: computation is what the AI prompt is
for. Naming the empty inhabitant of a declared type is a literal, in the same family as
true and 5.

Format changes

  • BAST FORMAT_REVISION 21 — value tag 12. Regenerate .bast files with this riddlc.
  • JSON{"value":"empty"}, with an optional type for the ascribed form.
  • EBNFempty_value added, with a guard preventing an unascribed empty from
    swallowing the following statement.