The link operator¶
The link operator is the three-part form
[[alice]] -> [[bob]] {
relation: parent
confidence: 0.9
}
-> constructs an edge, the brackets on either side name its endpoints, and
{..} is the data carried on it. A bracket pair on its own is not this operator:
[[bob]] is a doc reference and asserts nothing.
[[..]] is a doc reference¶
[[..]] written around a name references a document in the current namespace:
[[alice]] // a name in this namespace
[[people/alice]] // a name qualified by module
[[alice#Bio]] // a heading fragment
[[alice#^claim]] // an anchored block
The referenced document can be in any format the vault holds — Markdown, HMD, JSON, YAML, HTML. A reference addresses a document, and format is something the document has, not something the reference selects. Nor does it hand back a Card: card-ness is a knowledge property established by a checked narrowing, never by the name that points at the target. See doc-type for what a document is and card-type for the vault that resolves the name in it.
Inside the brackets is a name, not an expression: the operator has no variable
form, and addressing a target whose name is computed at runtime needs its own
spelling. Display text is HMD's, not HQL's — [[alice|Alice B.]] references the
same document as [[alice]], and whether HQL source accepts the pipe form at all
is open, since | is the pipe operator (see pipes).
An authored link has an implicit source¶
In a document body, a bare reference is an edge, because the document it sits
in supplies the missing endpoint. Written in alice:
[[bob]] {
color: red
relation: parent
}
means
[[alice]] -> [[bob]] {
color: red
relation: parent
}
[[this]] is the source, and [[this]] in alice is [[alice]]. This is what
gives a reference two readings without giving it two meanings. As a value,
x = [[bob]] is the reference. As an authored link, [[bob]] is that edge, and
[[bob]] with no braces is the same edge with empty data. Uplinks and downlinks
are the two directions of traversal over exactly these edges — see doc-type.
Authoring one adds it to the vault graph¶
The expansion is not only a reading. An authored link adds the edge to the
vault's graph, and the vault is where uplinks, downlinks and cards already
come from.
If the edge is already there, the write is an update rather than a second edge: the data in the braces is merged into the annotation the edge already carries. So the two links
[[bob]] { relation: parent }
[[bob]] { confidence: 0.9 }
leave one edge from alice to bob annotated with both keys, not two edges
side by side.
Three things follow, and they are consequences rather than separate decisions:
- An edge is identified by its endpoints. One
alice -> bobedge exists, and writing the link again addresses it. A vault that needed two distinct edges between the same pair — two different claims, kept apart — could not express it this way; it would need an edge identity beyond the pair, which is what aRelationCardsupplies. - The annotation accumulates. Data arrives from every place the link is authored, so the annotation on an edge is not necessarily any one author's statement.
- Forward links still work. The target need not exist, because the edge is keyed by the reference, not by a resolved document.
Open: whether the merge is per key at the top level or deep through the tree,
and what happens when two authored links give the same key different values. A
last-write-wins rule would make the annotation depend on vault traversal order,
which is the same objection that makes cards ordering a live question — see
card-type. Retaining both values is the reading consistent with knowledge
keeping conflicting claims, and that argues the conflict should surface rather
than resolve silently.
Also open: whether [[this]] is a keyword a program may write, and whether an
edge constructed in an HQL cell asserts into the graph the way an authored one
does. Constructing a value does not persist a document, so a cell that wrote
edges into the vault would be performing an effect, and that has to be explicit.
{..} is ordinary data¶
The braces are not graph syntax. They are the Data attached to the edge, and the keys in them are the author's:
Edge {
source: [[alice]]
target: [[bob]]
data: {
relation: parent
confidence: 0.9
}
}
relation, color and confidence are entries in a tree, not grammar. Nothing
in the core knows what relation: parent means, which is what keeps the edge
form from turning every vocabulary a vault invents into a language keyword — the
same reason the header is Data rather than a declared record.
The graph model behind it¶
The edge types belong to the graph extension, not to the core:
type Edge<S, T> {
source: S
target: T
data: Data
}
type Graph<N, E>
HMD specializes them for documents, and the knowledge extension specializes them independently for meaning:
type Link<S, T> : Edge<S, T>
type HmdGraph : Graph<Card, Link>
type Relation<S, T> : Edge<S, T>
type KnowledgeGraph : {Knowledge, Graph<Concept, Relation>}
That separation is the point. [[alice]] -> [[project-x]] is an edge in the
document graph; a semantic claim such as WorksOn(alice, project_x) is an edge
in a knowledge graph. Both render through the same graph machinery without being
treated as the same object. The layering is core → graph → knowledge, and HMD
sits beside them as one source of documents.
Data on an edge is not a semantic relation¶
An edge carries data; that is what {..} is for. What it does not do is make the
edge a claim. relation: parent is an author's entry in a tree, and reading it
as a typed relationship is an interpretation that has to be performed and
retained — see hql/knowledge.
So a link can carry data. What it cannot do is acquire identity or assert
meaning by carrying it. When a relationship needs identity, provenance,
evidence, discussion or incoming links of its own, it should stop being an edge
and become a RelationCard: an edge is cheap and anonymous, while a card is a
document other documents can point at — see card-type for the two kinds of
card and why this one takes no node position.
Failure¶
Resolution can fail in HMD's own ways: a target that does not exist, a fragment missing from a document that does, a name matching several documents, or a target escaping the namespace root. HMD treats an unresolved link as a warning by design — writing forward to a document that has not been written yet is permitted, and the diagnostic report is a to-do list. HQL must not turn that into a hard error by accident.
The edge form makes this concrete rather than settling it: an edge can be
constructed over an endpoint that does not resolve, and the endpoint is the
reference, not the document. Whether a reference used where a document is wanted
yields an Option, a value carrying its unresolved state, or a diagnostic is the
largest open question here. Since absence is Option and there is no null, the
reading hardest to defend is the one that quietly yields a Doc.
Also open: whether <- exists as the mirrored form, how -> binds against |,
and whether an endpoint may be an expression rather than a bracketed name.
See doc-type for uplinks and downlinks, card-type for the vault, hql/knowledge for relation cards, data-type for the edge's data and hql/design-status for what remains undecided.