Connectors and Sections
In the Atomese type hierarchy, connectors and sections generalize the notion of of types and functions. The provide a way of describing things that are "connectable", where the connectors are "typed" in such a way that only matching types can connect. Connectors and sections provide the base types for the type constructors, as they generalize the notion of a function type.
As this is a fairly abstract concept, some informal examples are given first. Please be aware that the explanation may seem "trivial" or "obvious"; that is because "connecting things together" is an obviously plain idea. It's belabored here, because getting the Atomese code and implementation to work correctly is very far from ordinary, plain or obvious.
A detailed formal development of these ideas can be found in the following PDF's:
... and a few others.
Consider the C++ function
- int f(std::string s)
It is meant to indicate a callable function, taking a string argument and returning an int. It can be composed with strings, or with any function that returns string. For example, given
- std::string g(double x)
one can validly compose them in order
- int n = f(g(3.14));
Not all functions can be composed: one cannot write because the types don't match. Functions are typed. Function calls are always directional: there is an "input" and an "output", and thus they can be drawn as arrows. Note that function composition always connect head-to-tail, and yields a tree -- a "directed acyclic graph" (DAG). One cannot connect head-to-head or tail-to-tail.
Consider a tensor
The rules for contracting tensor indexes is that they must match. For example,
is a valid tensor, because the index can be contracted. It is shorthand notation for
The is the product or contraction of the tensors and . The indexes that were contracted disappear.
Consider a tensor
The roman vs. greek tensor indexes are meant to indicate that the indexes are of a different type, so that one can never connect (contract) them together. Thus, just as in the function example above, the index types determine what may be connected and what cannot be. Unlike the function example, tensor indexes are not directional; they are not arrows. There is no "input" and "output", there is no flow "from" and "to", "head" and "tail". There is a distinction between upper and lower indexes (covariant and contravariant) and the rules of the game are that you can only contract upper-to-lower. However, this is not an arrow-like direction, it is rather a polarity. If one draws a line between the contracted indexes, one gets a graph: each tensor is a vertex in the graph; each contracted index-pair is an edge. The resulting graph is not a DAG! If one did use arrows to draw connections from contra- to co-variant indexes, one will in general obtain a graph with loops.
Consider the Link Grammar dictionary
Mary: S+; saw: S- & O+; a: D+; bird: D- & O-;
These can be assembled into a parsed sentence, by treating these dictionary entries as if they were tensors, and the labelled letters as contra/covariant indexes, of differing types. Thus, S indicates a subject-verb relationship, and O indicates a verb-object relationship, while D indicates a determiner. The plus-and-minus signs indicate polarity, and specifically, a left-right order. The resulting contraction can be drawn as:
+----O----+ +--S--+ +--D--+ | | | | Mary saw a bird
with the contracted index types indicated with link types.
All of the above cases have a common theme: things that can connect to other things. Such connectability is represented in Atomese with several different kinds of types: Connector, ConnectorSeq, Section and a few others. These have the general structure:
Section <entry (possibly lexical)> ConnectorSeq Connector <optional bond name> <bond type (possibly lexical)> <bond properties (polarity, other constraints)> Connector ...
LambdaLink ; a Section VariableList ; a ConnectorSeq TypedVariable ; a Connector VariableNode "X" ; AnyNode, no naming restrictions TypeNode 'Concept ; LexicalNode, a simple type TypedVariable VariableNode "Y" TypeNode 'Concept <body of the lambda> ; at the end, not the beginning
which roughly stands for in lambda calculus. The type declarations provide a signature for what the LambdaLink accepts as input. As an example of a Section, the various parts are:
Sections can be used directly to represent grammars. From the third example, the verb saw would have a lexical entry of the form:
Section LexicalNode "saw" ConnectorSeq Connector BondNode "S" ConnectorDir "-" Connector BondNode "O" ConnectorDir "+"
as the Atomese equivalent of the saw: S- & O+; lexical entry.