A Value is used to assign valuations to Atoms. Multiple, different valuations can be associated to a single atom, which can be accessed with a key. The key can be any other atom. In essence, every atom has an associated key-value store. The triple (atom, key, value) is called a Valuation. In the atom type hierarchy, Values inherit from ProtoAtom.
Values can be lists of floats (FloatValue), lists of strings (StringValue), or lists of values (LinkValue). There are two tyopes of values that get special treatment in the code: the various different TruthValues, and the AttentionValues. Both are backed by C++ classes that implement additional functions and behaviors.
(FloatValue 0.1 0.2 0.3) (StringValue "a" "bb" "ccc") (LinkValue (StringValue "foo") (FloatValue 41 43 43 44) (ConceptNode "barfoo"))
There is no IntValue or BooleanValue ... see discussion below for more information. Values and atoms both can be saved to a persistent store (currently, a postgress SQL backend).
Atoms vs. Values
In many ways, atoms and values are similar; however, they have very different performance profiles, and are intended to solve different knowledge-representation problems. Atoms are globally unique, and can be thought of as being static and unchanging. Atoms are immutable: they can only be created and destroyed; they cannot be changed. Atoms are indexed (by the AtomSpace) and are meant to encode generic graphs that can be rapidly traversed.
By contrast, Values, and valuations in general, should be thought of as a way of holding on to rapidly-changing data. For example, the (probability of the) truth of some proposition might change over time, as new evidence is gathered up. The proposition itself would be stored as an Atom; the truth value assigned to it would be stored as a Value, attached to that Atom.
Note that, due to the type hierarchy, all atoms are values, and so, if desired, one can use an atom wherever a value might go. The converse is not true: one cannot use values as if they were atoms.
Various extensions to the current system are possible.
There is no IntValue or BooleanValue; these don't seem to be useful or important for any current uses. These could be added, but if you feel you need them, you should re-examine your design and your thinking. Keep in mind that Atomese is meant to be a system for knowledge representation, and for data-structure introspection and re-writing. It is NOT meant to be a traditional "programming language": it is designed to be easy-to-understand by AI algorithms, not by humans. That is why we don't have IntValue or BooleanValue's so far.
Values are meant to store rapidly-changing data, but that does not mean that you actually have to rapidly update values all the time. For actual streaming data, where one is interested only in the most recent/current value, one should create a custom Value type that specifically knows how to access the streaming data (e.g. video-streaming or audio-streaming data). That is, the Value would would be backed by an underlying C++ class, which, among other things, would encode a URL to the streaming data, and know how to attach to that data source or data stream.
Note that data streams have two conceptual components: a long-term, static, unchanging part, that designates the data source (such as a URL), the data format, and then the actual data stream itself. The former could be encoded as Atoms, and actually held/stored in the AtomSpace. They could also serve as the "key" (see below) under which the value is located. Of course, the LinkValue would be used for structured data (e.g. streaming JSON data).
TruthValues are already backed by special-purpose C++ classes; these can be used as an example implementation.
Reasoning along similar lines, it might make sense to have a ROSValue type, which associates a ROS node to a particular value. These streaming data/ROS-node ideas remain hypothetical and are unimplemented, as of right now.
Space and Time Values
The SpaceServer is another subsystem that would work best, if implemented as a set of Values hanging on an atom, and various blobs of code that do interesting things with those values. That is, if one wanted to track the current location of a particular object, one could obtain those values from an underlying C++ SpaceValue object (derived from FloatValue) that supplied the actual x,y,z coordinates from an external system (e.g. a face-tracker or object-tracker).
Usage guidelines; current implementation
Valuations are triples, consisting of (atom, key, value) and intentionally resemble an EvaluationLink. Thus, it is strongly suggested that keys should always be PredicateNodes. One may conceptually envision that something like the following is floating about in the system:
Valuation PredicateNode "some-key" SomeAtom SomeValue
This states that "SomeValue" is associated with "SomeAtom" under the key of "PredicateNode some-key". In the actual, current implementation, values are stored in a key-value store, one per Atom. This means that there is no lock contention to fetch values; the values on multiple atoms can be simultaneously altered in different threads. Both access and update is essentially parallel.
Implementation and API
The C++ code that implements values and valuations is in /opencog/atoms/base. The scheme bindings for creating and working with values are defined in (use-modules (opencog)), that is, in the core module. The currently defined routines include:
cog-value ; Get the value for an (atom, key) pair. cog-set-value! ; Create a valuation triple (atom, key, value) cog-value? ; Return #t if the argument is an opencog value. cog-value-type? ; Return the type of a value. cog-new-value ; Create a new value cog-value->list ; Convert an opencog value to a scheme list. This discards the type of the value. cog-value-ref ; Get the n'th item in the value.
Note that guile also defines a concept of "value" but this is something completely different and unrelated!.
A list of all scheme functions pertaining to values can be obtained by typing
a, value at the guile prompt. Documentation for each individual functions can be found by typing
d, func-name. Recall that
a, is short for "apropos", and
d, is short for "describe".
The following design issues have not yet been addressed:
- Should cog-evaluate!, when called on an appropriate EvaluationLink return something other than a TruthValue? For example, if it is used with a GroundedPredicateNode that constructts some weird value -- shouldn't that be returned? How should that work? See SpaceServer for a practical example of a GPN that returns a PositionValue.
- If an EvaluationLink does return some non-TV value, how can this be used to create a valuation? That is, how can one force the evaluation of something like the below:
Valuation PredicateNode "some key" Concept "some atom" EvaluationLink GroundedPredicateNode "something that returns a FloatValue" ListLink Concept "foo" Concept "bar"
The goal of running teh above would be to run the evaluationLink, get the FloatValue it returns, and then stuff that into the valuation. How, exactly, is thus supposed to work?