Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Runtime Execution Models

Every definition you write describes places, and rules for filling them.

But not every place behaves the same way once it’s filled.

Consider two examples, side by side:

AND2[(A<> B<>)($OUT)
  $A $B :
  p,r[Z0]
  ...
]
REACTION[(H2<> O2<>)($WATER)
  @runtime(consumable)
  $H2 $O2 :
  ha,hb,oa[RXN]
  RXN[WATER<w1>]
  RXN[WATER<w2>]
]

Both use the same joint-match syntax. Both describe a rule that fires once its inputs are present. But they mean something different by “present.”


The default: a signal, once true, stays true

In AND2, once the place p holds a value, it holds that value for good. Nothing consumes it. Another rule can read p a hundred times without using it up — that’s exactly how a wire behaves. Once asserted, a signal doesn’t disappear because something downstream looked at it.

This is the model every definition gets automatically, with no annotation required. It’s the right model for anything that behaves like ordinary digital logic: a value is either present or it isn’t, and being read doesn’t change that.

@runtime(consumable): a token, once used, is gone

REACTION is doing something different. ha, hb, and oa aren’t signals sitting on wires — they’re meant to behave like reactant molecules. Once they combine to form RXN, they’re spent. They can’t also go on to power a second, unrelated reaction. If you fed the reactor two hydrogen molecules and one oxygen molecule, you’d expect exactly one water-forming event — not an ever-present “hydrogen is available” signal that keeps triggering new reactions forever.

@runtime(consumable), written once in a definition’s header, tells MatterScript that this definition’s places work that way: matching a rule removes the tokens that satisfied it. Feed in a second ha, hb, oa set later, and the rule can fire again — because presence, this time, really did mean “this specific instance is here right now,” not “this condition has become permanently true.”

Where to put it

The directive goes in the same header position as @domain and @generate — after the source and destination lists, before the resolution’s ::

REACTION[(H2<> O2<>)($WATER)
  @runtime(consumable)
  $H2 $O2 :
  ...
]

Leaving it out is not an oversight to fix — it’s the correct choice for the overwhelming majority of definitions, since ordinary digital logic is the default for a reason: it’s what nearly everything you’ll write actually is.

What actually changes underneath

You don’t need to think about this to use the directive, but it helps to know what’s different, the same way it helps to know an AST exists even if you never inspect one by hand.

An ordinary definition’s places are checked once and, once satisfied, stay satisfied — a rule’s inputs being true is a permanent fact about that run. A consumable definition’s places are tracked as a multiset: a running count of how many of each named token are currently available. Firing a rule decrements the count for everything it consumed and increments the count for whatever it produces. A rule that needed three tokens and produced two behaves exactly like a chemical equation balancing quantities, not a gate holding a state.

This also means a consumable definition can produce more than one output from a single firing — RXN[WATER<w1>] and RXN[WATER<w2>] both fire off the same reaction event, and both counts get recorded. An ordinary definition has no equivalent notion of “this place fired twice” — a place either became valid or it didn’t.

What you don’t need to design yourself

You don’t need to write two versions of your definition, or handle the bookkeeping of what’s been consumed. The runtime reads the tag once and picks the correct execution model automatically — you write the rules the same way either way, using the same joint-match and fill syntax throughout this book. The tag is the only thing that changes.

If a testbench or tool feeds a consumable definition input meant for the ordinary model (or vice versa), that mismatch is caught and reported plainly rather than silently producing a meaningless result — you’ll know immediately if a definition and the way it’s being driven have gotten out of sync.

When to reach for it

Ask whether your places represent conditions or quantities.

  • If a place being present means “this fact is now true, and stays true” — ordinary digital logic, dual-rail encoding, anything you’d eventually expect to become a real circuit — leave the definition untagged.
  • If a place being present means “exactly this many instances of this thing currently exist, and using one changes how many are left” — reactants, resources, anything with a notion of depletion or multiplicity — reach for @runtime(consumable).

There’s no hardware realization for the consumable model yet — no VHDL backend understands token depletion the way it understands a persistent signal. For now, @runtime(consumable) definitions run only against MatterScript’s reference runtime, as a way of expressing and testing ideas that don’t map onto a wire at all. That may change; it hasn’t yet, and the directive doesn’t pretend otherwise.