The DefineLink is used to give a unique name to an atom (a pattern, a function, a type, etc). When it a defined name is encountered during pattern matching, or during evaluation/execution, it is replaced by the the definition. It was added to atomese to make it easier for human coders, who are hand-writing atomese, to create human-readable, structured code. As such, it is not really central to atomese, and is best avoided, unless you really do need it for some purpose. For knowledge-representation tasks, it is suggested that the EquivalenceLink be used instead; it is better suited for associating declarative facts. DefineLink can be thought of as the imperative form of the EquivalenceLink. See the link comparison chart for other such correspondences.
The DefineLink is vaguely similar to 'define' in scheme, but also sharply different in that it is not scoped, and also, it is not mutable. Many users might benefit from using the StateLink instead of DefineLink.
There are three things that one may typically want to "define":
- (a) associate a name to some hypergraph, typically, a pattern, so that the named object can be used with the pattern matcher.
- (b) define a new link type, and, specifically, an imperative link type, so that executing the imperative link causes the same action to be performed as it's definition. You actually define a DefinedSchemaNode as you need a name for the new 'link'.
- (c) define a new type, using DefinedTypeNode.
The DefineLink does NOT bind any variables inside of it; these remain free. Thus, DefineLink can be used when creating more complex structures containing common (shared) variables between them. To bind variables, use the LambdaLink or the FunctionLink, as shown in the DefinedSchemaNode example, below.
DefineLink is similar to StateLink, which also allows only one definition at a time; the difference being that DefineLink will throw an error if a second definition is attempted, whereas the StateLink will automatically delete the old definition.
DefineLink inherits from UniqueLink, which provides the actual mechanics implementing uniqueness.
DefineLink <Name> <Definition>
Note that in spite of this limitation one can use DefineLink such that the Definition is any atom type, by considering DefinedSchemaNode as a nullary schema defining a constant. In that case there is no need to use ExecutionOutputLink to get the constant out of a DefinedSchemaNode, the DefinedSchemaNode is the constant itself. See here for an example.
Uniqueness of name
Given for instance
DefineLink DefinedPredicateNode "NewName" PredicateNode "My predicate"
DefineLink DefinedPredicateNode "NewName" PredicateNode "My other predicate"
would not be permitted because the DefinedPreidcateNode "NewName" has already a definition, the PedicateNode "My predicate".
The C++ DefineLink class offers the get_definition() method to get the definition associated to a DefineLink.
The following uses DefineLink to define a DefinedPredicateNode
DefineLink DefinedPredicateNode "MyPredicate" AndLink ...
The body of the AndLink should be any collection of atoms that, when evaluated, can be And'ed together. In place of the AndLink, one could use any TV-valued atom, such as OrLink, NotLink, AbsentLink, and so-on.
During pattern matching, when a "bare" DefinedPredicateNode is encountered, it will be checked to see if it has a definition. If it does, then it will be expanded, and the definition will be used in it's stead.
A different example, of using DefinedPredicates for numerical computation, is worked in detail in the PredicateFormulaLink page.
In some cases, one wants to implement numeric formulas that result in atoms, not values, and so PredicateFormulaLink is inappropriate. In this case, one may use a DefineLink to define a DefinedSchemaNode. Note that DefineLink itself does not bind any variables; thus, a FunctionLink (a special case of LambdaLink) is used to perform the desired variable binding. For example:
DefineLink DefinedSchemaNode "x+y*10" LambdaLink VariableList VariableNode "$X" VariableNode "$Y" PlusLink VariableNode "$X" TimesLink VariableNode "$Y" NumberNode 10
One could then use this definition to execute "x+y*10"
ExecutionOutputLink DefinedSchemaNode "x+y*10" ListLink NumberNode 2 NumberNode 4
by using cog-execute! The following works today:
(cog-execute! (ExecutionOutputLink (DefinedSchemaNode "x+y*10") (ListLink (NumberNode "2") (NumberNode "4")))
Running this returns
(explicitly tested in /tests/scm/SCMExecutionOutputUTest.cxxtest)
DefinedSchemaNode as constant
One may use DefinedSchemaNode to define a nullary schema (that is a constant) as well.
DefineLink DefinedSchemaNode "MyList" ListLink NumberNode "2" NumberNode "4"
And use the DefinedSchemaNode directly in place of the ListLink such as
(cog-execute! (ExecutionOutputLink (DefinedSchemaNode "x+y*10") (DefinedSchemaNode "MyList")))
which should return (TODO: not tested yet)