Crate asdi[][src]

Expand description

Another Simplistic Datalog Implementation (ASDI), in Rust.

This package provides a data model to represent Datalog programs in memory, a parser for the textual representation, and some evaluation implementations.

The text representation parser is a separate feature, so if you only need to construct and evaluate programs using the API you may opt out of the Pest parser and support.

Datalog Defined

Datalog is a logic programming language and a subset of the earlier Prolog. Chapter 1 of Logic Programming and Databases provides a good overview of the drawbacks of Prolog and the advantages of Datalog for certain tasks.

When referring to the specifics of the language we will use the common format $\text{\small{Datalog}}$ with superscripts that identify specific language extensions; for example, $\small\text{Datalog}^{\lnot}$ is the language extended with negation of literals, $\small\text{Datalog}^{\Gamma}$ is the language extended with type checking on attributes, and $\small\text{Datalog}^{\lnot,\theta}$. is the language extended with negation of literals and comparison expressions. The order of superscript symbols is irrelevant. Additionally, text in bold indicates a key concept in the language while text in italics indicates a forward reference to such a concept.

Abstract Syntax

Rules

Rules $\small R$ are built from a language $\small \mathcal{L}=( \mathcal{C},\mathcal{P},\mathcal{V})$ that contains the

  1. $\small \mathcal{C}$ – the finite sets of symbols for all constant values; e.g. hello, "hi" 123,
  2. $\small \mathcal{P}$ – the finite set of alphanumeric character strings that begin with a lowercase character; e.g. human, size, a,
  3. $\small \mathcal{V}$ – the finite set of alphanumeric character strings that begin with an uppercase character; e.g. X, A, Var.

While it would appear that the values from $\small \mathcal{P}$ or $\small \mathcal{V}$ would overlap, these values must remain distinct. For example, the value human is a valid predicate and string constant but they have distinct types in ASDI that ensure they are distinct.

Each rule $\small r \in R$ has the form:

$$\tag{i}\small A_1, \ldots, A_m \leftarrow L_1, \ldots, L_n$$

as well as the following properties:

  1. $\small head(r)$ (the consequence), returns the set of atom values $\small A_1, \ldots, A_m$ where $\small m \in \mathbb{N}$,
  2. $\small body(r)$ (the antecedence), returns the set of literal values $\small L_1, \ldots, L_n$ where $\small n \in \mathbb{N}$,
  3. $\small distinguished(r)$ returns the set of terms in the head of a rule, $$\tag{ii}\small distinguished(r) \coloneqq \lbrace t | t \in \bigcup\lbrace terms(a) | a \in head(r) \rbrace \rbrace$$
  4. $\small non\text{-}distinguished(r)$ returns the set of terms in the body that of a rule that are not in the head, $$\tag{iii}\small non\text{-}distinguished(r) \coloneqq \lbrace t | t \in ( \bigcup\lbrace terms(a) | a \in body(r) \rbrace - distinguished(r) \rbrace)\rbrace$$
  5. $\small ground(r)$ returns true if its head and its body are both ground: $$\tag{iv}\small ground(r) \coloneqq (\forall{a}\in head(r); ground(a)) \land (\forall{l}\in body(r); ground(l))$$
  6. $\small positive(r)$ returns true if all body literals are positive: $$\tag{v}\small positive(r) \coloneqq (\forall{l}\in body(r); positive(l))$$

A pure rule is one where there is only a single atom in the head; if the body is true, the head is true. A constraint rule, or contradiction, does not allow any consequence to be determined from evaluation of its body. A disjunctive rule is one where there is more than one atom, and any one may be true if the body is true. The language $\small\text{Datalog}^{\lor}$ allows for inclusive disjunction, and while a language, $\small\text{Datalog}^{\oplus}$, exists for exclusive disjunction it is not implemented here.

The property $\small form(r)$ returns the form of the rule, based on the cardinality of the rule’s head as follows:

$$\tag{vi}\small form(r) \coloneqq \begin{cases} pure, &\text{if } |head(r)| = 1 \\ constraint, &\text{if } |head(r)| = 0 \land \text{Datalog}^{\lnot} \\ disjunctive, &\text{if } |head(r)| > 1 \land \text{Datalog}^{\lor} \end{cases}$$

Note that this notation is similar to that of a sequent. Taking our definition of a rule, $\small A_1, \ldots, A_m \leftarrow L_1, \ldots, L_n$, and swap the order of antecedence and consequence we get $\small L_1, \ldots, L_m \vdash A_1, \ldots, A_n$. A pure rule is termed a simple conditional assertion, a constraint rule is termed an unconditional assertion, and a disjunctive rule is termed a sequent (or simply conditional assertion).

Terms

Terms, mentioned above, may be constant values or variables such that $\small\mathcal{T}=\mathcal{C}\cup\mathcal{V}\cup\bar{t}$ where $\small\bar{t}$ represents an anonymous variable.

Terms have the following properties:

  1. $\small constant(t)$ returns true if the term argument is a constant value.
  2. $\small variable(t)$ returns true if the term argument is a variable.
  3. $\small anonymous(t)$ returns true if the term argument is the anonymous variable, $\small\bar{t}$.

With the definition of rules so far it is possible to write rules that generate an an infinite number of results. To avoid such problems Datalog rules are required to satisfy the following Safety conditions:

  1. Every variable that appears in the head of a clause also appears in a positive relational literal (atom) in the body of the clause. $$\tag{vii}\small \begin{alignat*}{2} safe\text{-}head(r) &\coloneqq &&\lbrace t | t \in distinguished(r), t \in \mathcal{V} \rbrace \\ &- &&\lbrace t | t \in \bigcup\lbrace terms(a) | a \in body(r), atom(a), positive(a) \rbrace, t \in \mathcal{V} \rbrace \\ &= &&\empty \end{alignat*}$$
  2. Every variable appearing in a negative literal in the body of a clause also appears in some positive relational literal in the body of the clause. $$\tag{viii}\small \begin{alignat*}{2} safe\text{-}negatives(r) &\coloneqq &&\lbrace t | t \in \bigcup\lbrace terms(a) | a \in body(r), \lnot positive(a) \rbrace, t \in \mathcal{V} \rbrace \\ &- &&\lbrace t | t \in \bigcup\lbrace terms(a) | a \in body(r), atom(a), positive(a) \rbrace, t \in \mathcal{V} \rbrace \\ &= &&\empty \end{alignat*}$$

Atoms

Atoms are comprised of a label, $\small p \in \mathcal{P}$, and a tuple of terms. A set of atoms form a Relation if each conforms to the schema of the relation. The form of an individual atom is as follows:

$$\tag{ix}\small p(t_1, \ldots, t_k)$$

as well as the following properties:

  1. $\small label(a)$ returns the predicate $\small p$,
  2. $\small terms(a)$ returns the tuple of term values $\small t_1, \ldots, t_k$; where $\small t \in \mathcal{T}$ and $\small k \in \mathbb{N}^{+}$,
  3. $\small arity(a)$ returns the cardinality of the relation identified by the predicate; $\small arity(a) \equiv |terms(a)| \equiv k$,
  4. in $\small\text{Datalog}^{\Gamma}$:
    1. there exists a type environment $\small \Gamma$ consisting of one or more types $\small \tau$,
    2. each term $\small t_i$ has a corresponding type $\small \tau_i$ where $\small \tau \in \Gamma$,
    3. $\small type(t)$ returns the type $\small \tau$ for that term,
    4. $\small types(a)$ returns a tuple such that; $\small (i \in {1, \ldots, arity(a)} | type(t_i))$,
  5. $\small ground(a)$ returns true if its terms are all constants: $$\tag{x}\small ground(a) \coloneqq (\forall{t}\in terms(a); t \in \mathcal{C})$$

Relations

Every relation $\small r$ has a schema that describes a set of attributes $\small \lbrace \alpha_1, \ldots, \alpha_j \rbrace$, and each attribute may be named, and may in $\small\text{Datalog}^{\Gamma}$ also have a type.

Relations have the following properties:

  1. $\small label(r)$ returns the predicate $\small p$,
  2. $\small schema(r)$ returns the set of attributes $\small \lbrace \alpha_1, \ldots, \alpha_j \rbrace$; where $\small k \in \mathbb{N}^{+}$,
  3. $\small arity(r)$ returns the number of attributes in the relation’s schema, and therefore all atoms within the relation; $\small arity(r) \equiv |schema(a)| \equiv j$.

Attributes have the following properties:

  1. $\small label(\alpha)$ returns either the predicate label of the attribute, or $\small\bot$.
  2. in $\small\text{Datalog}^{\Gamma}$:
    1. $\small type(\alpha)$ returns a type $\small \tau$ for the attribute, where $\small \tau \in \Gamma$, or $\small\bot$.

The following defines a binary function that determines whether an atom $\small a$ conforms to the schema of a relationship $\small r$.

$$\tag{xi}\small \begin{alignat*}{2} conforms(a, r) &\coloneqq &&ground(a) \\ &\land &&label(a) = label(r) \\ &\land &&arity(a) = arity(r) \\ &\land &&\forall{i} \in \lbrace 1, \ldots, arity(r)\rbrace \medspace conforms( a_{t_i}, r_{\alpha_i} ) \end{alignat*} $$ $$\tag{xii}\small conforms(t, \alpha) \coloneqq label(t) = label(\alpha) \land \tau_{t} = \tau{\alpha} $$

Note that in relational algebra it is more common to use the term domain $\small D$ to denote a possibly infinite set of values. Each attribute on a relation has a domain $\small D_i$ such that each ground term is a value $\small d_i$ and the equivalent of $\small \tau_i \in \Gamma$ becomes $\small d_i \in D_i$.

To visualize a set of facts in a relational form we take may create a table $p$, where each column, or attribute, corresponds to a term index $1 \ldots k$. If the facts are typed then each column takes on the corresponding $\tau$ type. Finally each row in the table is populated with the tuple of term values.

$\small col_1: \tau_1$$\small \ldots$$\small col_k: \tau_k$
$\small row_1$$\small t_{1_1}$$\small \ldots$$\small t_{1_k}$
$\small \ldots$$\small \ldots$$\small \ldots$$\small \ldots$
$\small row_y$$\small t_{y_1}$$\small \ldots$$\small t_{y_k}$

Literals

Literals within the body of a rule, represent sub-goals that are the required to be true for the rule’s head to be considered true.

  1. A literal may be an atom (termed a relational literal) or, in $\small\text{Datalog}^{\theta}$, a conditional expression (termed an arithmetic literal),
  2. a an arithmetic literal has the form $\small \langle t_{lhs} \theta t_{rhs} \rangle$, where
    1. $\small \theta \in \lbrace =, \neq, <, \leq, >, \geq \rbrace$,
    2. in $\small\text{Datalog}^{\Gamma}$ both $\small t_{lhs}$ and $\small t_{rhs}$ terms have corresponding types $\small \tau_{lhs}$ and $\small \tau_{rhs}$,
    3. the types $\small \tau_{lhs}$ and $\small \tau_{rhs}$ must be compatible, for some system-dependent definition of the property $\small compatible(\tau_{lhs}, \tau_{rhs}, \theta)$,
  3. in $\small\text{Datalog}^{\lnot}$ a literal may be negated, appearing as $\small \lnot l$,
  4. and has the following properties:
    1. $\small relational(l)$ returns true if the literal argument is a relational literal.
    2. $\small arithmetic(l)$ returns true if the literal argument is a arithmetic literal.
    3. $\small terms(l)$ returns either the set of terms in either the atom or comparison, $$\tag{xiii}\small terms(l) \coloneqq \begin{cases} terms(l), &\text{if } atom(l) \\ \lbrace t_{lhs}, t_{rhs} \rbrace, &\text{if } comparison(l) \land \text{Datalog}^{\theta} \end{cases}$$
    4. $\small ground(l)$ returns true if its terms are all constants $\small (\forall{t}\in terms(l); t \in \mathcal{C})$,
    5. $\small positive(l)$ in $\small\text{Datalog}^{\lnot}$ returns false if negated, otherwise it will always return true.

Facts

Any ground rule where $\small m=1$ and where $\small n=0$ is termed a Fact as it is true by nature of having an empty body, or alternatively we may consider the body be comprised of the truth value $\small\top$.

$$\tag{xiv}\small fact(r) \coloneqq (ground(r) \land form(r)=pure \land body(r)=\empty)$$

Queries

An atom may be also used as a Goal or Query clause in that its constant and variable terms may be used to match facts from the known facts or those that may be inferred from the set of rules introduced. A ground goal is simply determining that any fact exists that matches all of the constant values provided and will return true or false. In the case that one or more variables exist a set of facts will be returned that match the expressed constants and provide the corresponding values for the variables.

The set of facts (ground atoms) known a-priori is termed the Extensional database, EDB, or $\small D_E$,. The set of rules, and any inferred facts, are termed the Intensional database, IDB, or $\small D_I$.

A Datalog Program $\small P$ is a tuple comprising the extensional database $\small D_{E}$, the intensional database $\small D_{I}$, and a set of queries.

$$\tag{xv}\small P=( D_E, D_I, Q )$$

This implies, at least, that the set of predicates accessible to queries in the program is the union of predicates in the extensional and intensional databases.

$$\tag{xvi}\small \mathcal{P}_P = \mathcal{P}_E \cup \mathcal{P}_I$$

It should be obvious that the same exists for constants and variables; $\small \mathcal{C}_P = \mathcal{C}_E \cup \mathcal{C}_I$ and $\small \mathcal{V}_P = \mathcal{V}_E \cup \mathcal{V}_I$.

Datalog does not, in general, allow the rules comprising the intensional database to infer new values for predicates that exist in the extensional database. This may be expressed as follows:

$$\tag{xvii}\small \mathcal{P}_E \cap \mathcal{P}_I = \empty$$

The same restriction is not required for constants in $\small \mathcal{C}_P$ or variables in $\small \mathcal{V}_P$ which should be shared.

Concrete Syntax

The definitions below use ABNF (Augmented BNF for Syntax Specifications) and focus both on the concrete syntax as expressed in the text representation. The ABNF definition is somewhat simplified from the grammar used in the ASDI parser although any deviations do not significantly affect the meaning of the language.

Programs

A program consists of a set of facts that comprise the extensional database, a list of rules that comprise the intensional database, and possibly a set of queries to interrogate the result of any reasoning performed over the program.

program         = *[ pragma ] *[ fact / rule / query ]

A program consists of a single file containing facts, rules, and queries as well as any additional files referenced via pragmas.

Facts

Facts must be expressed in the form of ground atoms.

fact            = predicate [ constant-list ] "."
predicate       = LC_ALPHA *[ ALPHA / DIGIT / "_" ]
constant-list   = "(" [ constant *[ "," constant ] ] ")"

The following demonstrates a simple fact denoting that the constant brooke representing some individual is the parent of some individual represented by the constant "Xerces".

parent("Xerces", brooke).

Constant Values

Constants are supported in three types, String, Integer, and Boolean. Whereas some definitions of Datalog introduce an additional Identifier type, ASDI treats these as short strings that can safely be expressed without quotes; therefore, the values xerces and "xerces" are equivalent.

constant        = string / integer / boolean
string          = short-string / quoted-string
short-string    = predicate [ ":" ALPHA *[ ALPHA / DIGIT / "_" ] ]
quoted-string   = DQUOTE ... DQUOTE
integer         = +DIGIT
boolean         = "@true" / "⊤" / "@false" / "⊥"

Boolean values may also be represented using (down tack \u{22a4}) for true, and (up tack \u{22a5}) for false where this may improve readability.

Rules

As facts are syntactically distinct from rules in the text representation there is no need for empty bodies – all rules must have at least one literal. Material implication may be written using the Unicode character (long leftwards arrow\u{27f5}).

rule            = head implication body "."
head            = [ atom *[ disjunction atom ] | "⊥" ]
disjunction     = ";" / "|" / "OR" / "∨"
implication     = ":-" / "<-" / "⟵"
body            = literal-list

The following rules are all equivalent.

ancestor(X, Y) :- parent(X, Y).
ancestor(X, Y) <- parent(X, Y).
ancestor(X, Y) ⟵ parent(X, Y).

As described in the abstract syntax it is an error to use an extensional relation in the head of a rule. The following will generate an error:

parent("Xerces", brooke).

parent(X,Y) :- father(X,Y).

The language feature disjunction corresponds to the language $\small\text{Datalog}^{\lor}$ and allows multiple atoms to appear in the rule’s head with the semantics that these are choices. This syntax will not be accepted unless the feature is enabled.

For example, the following describes the rule that if X is a parent then X is either a father or mother.

@feature(disjunction).

father(X) ⋁ mother(X) :- parent(X).

The language feature constraints corresponds to the language $\small\text{Datalog}^{\Leftarrow}$ and allows the specification of rules with no head. In this case the material implication symbol is required, the falsum value is optional for readability, therefore the following rules are equivalent.

@feature(constraints).

:- alive(X) AND dead(X).
⊥ ⟵ alive(X) ∧ dead(X).

ASDI will disallow the addition of rules that are unsafe according to the abstract syntax. The following are examples of unsafe rules:

  • a(X) :- b(Y). – because X appears as distinguished variable but does not appear in a positive relational literal.
  • a(X) :- b(Y), NOT b(X). – because X appears in negated literal but does not appear in a positive relational literal.
  • a(X) :- b(Y), X < Y. – Because X appears in an arithmetic literal but does not appear in a positive relational literal.

Atoms

The text representation of an atom is a relatively simple translation from the abstract syntax above.

atom            = predicate term-list
term-list       = "(" term *[ "," term ] ")"
term            = variable / constant
variable        = named-variable / anon-variable
named-variable  = UC_ALPHA *[ ALPHA / DIGIT / "_" ]
anon-variable   = "_"

The following are all atoms.

dead(julius_caesar).
emperor(julius_caesar, rome).
emperor(X, Y).
emperor(X, rome).

Literals

Any valid atom is also a valid positive literal. The syntax below also allows for negative literals as well as comparison expressions as literals. Conjunction may be written with the Unicode character (logical and \u{2227}).

literal-list    = literal *[ conjunction literal ]
literal         = [ negation ] atom / comparison
negation        = "!" / "NOT" / "¬"
conjunction     = "," / "&" / "AND" / "∧"

The following rules are all equivalent.

ancestor(X, Y) ⟵ parent(X, Z), ancestor(Z, Y).
ancestor(X, Y) ⟵ parent(X, Z) & ancestor(Z, Y).
ancestor(X, Y) ⟵ parent(X, Z) ∧ ancestor(Z, Y).
ancestor(X, Y) ⟵ parent(X, Z) AND ancestor(Z, Y).

The language feature negation corresponds to the language $\small\text{Datalog}^{\lnot}$ and allows the specification of negated literals. Negation may also be written using the Unicode character (full-width not sign \u{ffe2}). The following rules are equivalent.

@feature(negation).

alive(X) :- NOT dead(X).
alive(X) ⟵ ¬dead(X).

Comparisons

The language feature comparisons corresponds to the language $\small\text{Datalog}^{\theta}$ and allows the use of arithmetic literals. Comparisons take place between two literals and are currently limited to a set of common operators.

comparison      = term operator term
operator        = "=" / "!=" / "/=" / "≠" / "<" / "<=" / "≤" / ">" / ">=" / "≥"

The Unicode characters (not equal to \u{2260}), (less-than or equal to \u{2264}), and (greater-than or equal to \u{2265}) may be substituted for the common comparison operators.

All comparison operations must be between terms of the some type, such that the property compatible introduce above is defined as:

$$\tag{xvi}\small compatible(\tau_{lhs}, \tau_{rhs}, \theta) \leftarrow \tau_{lhs} = \tau_{rhs}$$

Additionally, some operators are not present for all types, as shown in the table below.

Type=, <, , >,
StringYesYes - lexical
IntegerYesYes
BooleanYesNo

The following is an example using comparison of some numeric attribute of the car relation.

@feature(comparisons).

antique(X) :- car(X, Y) AND Y > 50.

Queries

A query is simply an atom, but one identified to the system as a goal with either the prefix ?- or the suffix ?.

query           = ( "?-" atom "." ) / ( atom "?" )

The following queries are equivalent and will return the value of the variable X for any facts in the ancestor relationship where the first attribute is the string value "xerces".

?- ancestor(xerces, X).
ancestor(xerces, X)?

When the value _ is used in a query it denotes an attribute of the relation that has no meaning in either the query or the response. For example, in the following query we ask for all values of the model attribute in the car relation where the make is “ford”, and ignore the age entirely.

@assert car(make: string, model: string, age: integer).

car("ford", Model, _)?

The results of this query would not include the age column:

+------------+
| Model      |
+============+
| edge       |
+------------+
| escort     |
+------------+
| fiesta     |
+------------+
| focus      |
+------------+
| fusion     |
+------------+
| mustang    |
+------------+
     ...

Pragmas

TBD.

pragma          = feature / assert / infer / input / output

feature         = "@feature" feature-list "."
feature-list    = "(" feature-id *[ "," feature-id ] ")"
feature-id      = "comparisons" / "constraints" / "disjunction" / "negation"

assert          = "@assert" predicate attribute-list "."
infer           = "@infer" ( predicate attribute-list / infer_from ) "."
attribute-list  = "(" attribute-decl *[ "," attribute-decl ] ")"
attribute-decl  = [ predicate ":" ] attribute-type
attribute-type  = "boolean" / "integer" / "string"
infer_from      = "from" predicate

input           = "@input" io-details "."
output          = "@output" io-details "."
io-details      = "(" predicate "," file-name [ "," file-type ] ")"
file-name       = quoted-string
file-type       = quoted-string

The feature pragma determines which Datalog language is in use. Use of syntax not supported by the selected language feature will result in errors.

@feature(negation).
@feature(comparisons, disjunction).

The assert pragma describes a new relation in the extensional database. The parser can determine the schema for facts from their types in the database. The use of this pragma is therefore optional, but recommended.

@assert human(name: string).

The infer pragma describes a new relation in the intensional database. Typically the parser can determine the schema for relational literals from their context, The use of this pragma is therefore optional, but recommended. The alternate form is more explicit in that it defines an intensional relation in terms of a previously defined extensional relation.

@infer mortal(name: string).
# alternatively ...
@assert human(name: string).
@infer mortal from human.

The input pragma instructs the parser to load facts for the named extensional relation from an external file. This pragma requires that the relation be previously defined via the assert pragma.

@assert human(name: string).
@input(human, "data/humans.csv", "csv").

The output pragma instructs the parser to write facts from the named intensional relation to an external file. This pragma requires that the relation be previously defined via the infer pragma.

@infer mortal(name: string).
@output(mortal, "data/mortals.txt").

Comments

Comments in Datalog are identified by the # character and continue to the end of the line.

# Here's a comment
?- ancestor(xerces, X). # and another

Example

The following program is the classical syllogism example, in the text representation.

human("Socrates").

mortal(X) <- human(X).

?- mortal("Socrates").

Note in this example we allow the parser to identify the schema for the relations human and mortal rather than using the pragmas assert and infer.

The following is the same example constructed via the ASDI library.

use asdi::{PredicateSet, Program};
use asdi::edb::{Attribute, Predicate};
use asdi::idb::{Atom, Term, Variable};
use std::str::FromStr;

let mut syllogism = Program::default();

let predicates = syllogism.predicates();
let p_human = predicates.fetch("human").unwrap();
let p_mortal = predicates.fetch("mortal").unwrap();

let human = syllogism
    .add_new_extensional_relation(p_human.clone(), vec![Attribute::string()])
    .unwrap();
human.add_as_fact(["Socrates".into()]).unwrap();

let var_x: Term = Variable::from_str("X").unwrap().into();

syllogism
    .add_new_pure_rule(
        p_mortal.clone(),
        [var_x.clone()],
        [Atom::new(p_human, [var_x]).into()],
    )
    .unwrap();

syllogism
    .add_new_query(p_mortal, ["Socrates".into()])
    .unwrap();

The execution of this program will start with the goal query “is Socrates mortal?” and in doing so will evaluate the necessary rule and derive the relation mortal. The result is a boolean value denoting whether the goal is satisfied.

+------------+
| _: boolean |
+============+
| @true      |
+------------+

However, if we were to change the final query to replace the constant with a variable, as follows.

?- mortal(X).

The program will select all matching (in this case all) facts from the mortal relation.

+------------+
| X: string  |
+============+
| "Socrates" |
+------------+

Modules

This module provides the set of types that primarily describe the Extensional Database (EDB).

This module provides the common Error and Result types for this library.

This module provides the Feature and FeatureSet types that allow programs to support specific extensions to Datalog.

This module provides the set of types that primarily describe the Intensional Database (IDB).

This module provides the set of types that are used to implement formatted input/output operations for Relations.

This module provides the type Parsed that contains a program parsed from the text representation as well as the parse_file, parse_str, and parse_str_with_features functions.

Structs

The predicate set $\small\mathcal{P}$ determines the labels of relations, atoms, and facts. This type keeps a mapping of strings to PredicateRefs to reduce memory duplication.

A program consists of a set of extensional Relations, a set of intensional Relations, a set of Rules, and a set of queries.

Traits

Attributes, the members of Schema are named using different types in Relations and Views. This trait identifies the minimum set of implementations required for an attribute name.

All collections of things in the library implement these basic methods.

All indexed collections of things in the library implement these basic methods.

Implemented by types that have, for sure, a label. This type is mutually exclusive with MaybeLabeled.

Implemented by types that have the notion of an anonymous value.

Implemented by elements of the IDB that need to distinguish between values containing only constants and those that contain at least one variable.

Implemented by types that may have a label; note that this infers the existence of an anonymous value used when an instance has no label.

Implemented by types that need to distinguished between values that may contain negative literals.

All mutable collections of things in the library implement these basic methods.

All mutable, indexed, collections of things in the library implement these basic methods.

Commonly used properties of programs and rules.