Dandelion — subsystem

← All subsystems

Dandelion

A graph database over relations

Stems · bundles · closures · programs are graphs

A graph language where everything is a stem: a subject, a link and an object, ended by a period. Declarations, rules and questions are all bundles of stems, so a Dandelion program is itself a graph — one you can query with Dandelion.

Supply:
  S   key   SNO.
  P   key   PNO.
  SP  from  SNO.
  SP  to    PNO.

? who ships a part that S1 ships:
  'S1'  SP     _part.
  _s    SP     _part.
  _s    isnt   'S1'.
  _s    SNAME  ?NAME.

Why a dandelion

A dandelion seed head is a graph in plain sight: one centre, many stems, a seed at the end of each one, and every seed free to land somewhere else and start another. The language is named after it, and built from the same part: the stem.

One shape: the stem

A stem is three terms and a period:

subject  link  object.

There is nothing else. A stem can state a fact, constrain a question or define part of a rule, and it is written the same way every time. The terms are:

TermIs
SP, keya name: a relation, an attribute, a rule or a builtin
'S1', 42a value
?NAMEan answer variable: it becomes an attribute of the result
_parta hidden variable: it joins, then disappears

A link is anything that relates two things. A relation that bridges two islands is a link; so is an attribute, which relates a tuple to its value; so is a rule you wrote; and so are the builtins key, from, to, isnt and below.

Bundles

Stems are grouped into bundles: a name, a colon, and the stems indented beneath it. What a bundle means depends only on what is in it.

  • A bundle without variables states facts. A graph declaration is just facts about relations: which attribute keys an island, which attributes a bridge goes from and to.
  • A bundle with answer variables defines a relation, named after the bundle, whose attributes are those variables.
  • A bundle whose name starts with ? is a question: it defines the same kind of relation, and asks it now.
Backbone:
  Router  key   NAME.
  Link    from  SRC.
  Link    to    DST.

The Supply graph at the top of this page is Date’s suppliers and parts, exactly as pureSQL registers them under \relation. Nothing is copied: S, P and SP stay where they are, and the declaration is a way of reading them.

Links that grow

A link can be followed more than once, or backwards:

LinkReads
SPone step
~SPone step backwards
SP+one or more steps: the transitive closure
SP*zero or more steps: a thing reaches itself

A bundle with exactly two answer variables is a link of its own, so rules compose with closures. Here peer relates two suppliers that ship a common part, and peer+ follows that relation as far as it goes:

peer:
  ?A  SP    _part.
  ?B  SP    _part.
  ?A  isnt  ?B.

? everyone S1 is connected to through shared parts:
  'S1'  peer+  _s.
  _s    SNAME  ?NAME.

Programs are graphs

Every bundle you write is stored as stems, one tuple each, in the relation STEM (BUNDLE, SUBJECT, LINK, OBJECT). A tuple is an island and its attributes are links, so a Dandelion program can be asked about with Dandelion:

? which bundles follow SP:
  _stem  LINK    'SP'.
  _stem  BUNDLE  ?BUNDLE.

There is no separate reflection API, and no second syntax for metadata. Code and data have the same shape because both are graphs.

Why every question ends

Dandelion is Datalog-shaped: links relate things that already exist, and no rule builds a new value while it recurses. A closure is a least fixpoint — keep adding what one more step reaches until nothing new appears — over finite relations, so it always stops, and a cycle in the network adds nothing the second time around. Every program has a finite answer, and Sol can check that for any bundle you write.

In the same runtime

A bundle with answer variables is a relation, and its name is where it is published, under \relation. Nothing else is needed to share it:

REACHABLE:
  'core-1'  Link+  _r.
  _r        ZONE   'edge'.
  _r        NAME   ?NAME.
-- from pureSQL: join reachability with inventory
SELECT R.NAME, I.MODEL FROM REACHABLE AS R JOIN INVENTORY AS I USING (NAME);

Forth walks the same relation a tuple at a time with OPEN-RELATION, and because Dandelion reads relations live, a link inserted from pureSQL is a stem the next question follows.