Connectors and Sections

From OpenCog

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.

Informal examples


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.

In Atomese, function types are represented by the ArrowLink.


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.

In Atomese, there is currently no defined TensorLink, but there might be in the future!?

Link Grammar

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:

  +--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:

   <entry (possibly lexical)>
          <optional bond name>
          <bond type (possibly lexical)>
          <bond properties (polarity, other constraints)>


The LambdaLink provides an example of a function in Atomese. for example, an anonymous function taking two variables X and Y as input, which are constrained to be ConceptNodes, is written as

LambdaLink                      ; a Section
    VariableList                ; a ConnectorSeq
        TypedVariable           ; a Connector
            VariableNode "X"    ; AnyNode, no naming restrictions
            TypeNode 'Concept   ; LexicalNode, a simple type
            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:

   LexicalNode "saw"
          BondNode "S"
          ConnectorDir "-"
          BondNode "O"
          ConnectorDir "+"

as the Atomese equivalent of the saw: S- & O+; lexical entry.

See also