Chapter 8. Predicates

One of the basic constructs in LogiQL is the predicate. A predicate is a named set of tuples. For example, a predicate person may contain a set of one-tuples: {("Alice"), ("Bob"), ("Clara")}; a predicate friends may contain a set of two-tuples: {("Alice", "Clara")}. Tuples in a predicate must all have the same arity, or number of elements. This arity is also referred to as the arity of the predicate. For example, predicate person has arity one (and is also referred to as a unary predicate); predicate friends has arity two (and is also referred to as a binary predicate).

Note

  • It is possible to have a nullary predicate with arity 0: that is, the predicate is either empty, or contains only the nullary tuple ().
  • We often talk about populating a predicate. This is simply a convenient term for inserting tuples into the predicate.

For those familiar with relational databases, a predicate can be thought of as a table. There are two major differences between a predicate and a relational table:

  • A predicate contains a set of tuples, whereas a table contains a bag (a.k.a. multiset) of tuples. That is, a table may contain many duplicate copies of the same tuple, but each tuple in a predicate is different from all the others.

    Please note that inserting an existing tuple is not an error: it just won't change the state of the database.

  • A predicate does not contain NULLs. We encourage the normalization of schemata in order to represent optional data.

In the remainder of this chapter, we first cover the basics of declaring a predicate, and then the different types of predicates supported by the LogicBlox database, their declarations and the associated utilities.

8.1. Predicate Declaration

The existence of a predicate may be either explicitly declared or inferred (see Section 11.1.1, “Predicate Inference”). A predicate declaration specifies the predicate's name, its arity, and the type of values that may occupy each position in the tuple. We refer to these types as a predicate's argument types.

A basic predicate declaration has the following form:

PredicateDeclaration = Atom "->" Atom { "," Atom } "." .
Atom                 = Identifier "(" [ Identifiers ] ")" .
Identifiers          = Identifier { "," Identifier } .

The example below demonstrates the declarations of predicates person and friends.

Example 8.1. Basic predicate declarations

person(x) -> string(x).
friends(person1, person2) -> string(person1), string(person2).

The first declaration states that person is an unary predicate (its arity is one): it can contain only one-tuples. Furthermore, the first (and only) value in each tuple must be a value of type string.

The second declaration states that friends is a binary predicate (its arity is two): it can contain only two-tuples. Furthermore, the first value in each tuple must be a value of type string, as must the second value in each tuple.

A predicate's name, arity, and the type of its values is called the predicate's signature. Predicate declarations are used by the LogicBlox database to check a program for correctness, as well as to efficiently manage the storage of its contents and the evaluation of queries involving the predicate.

As readers will see in Chapter 16, Constraints, predicate declarations use the syntactic form of constraints. A constraint must satisfy very specific requirements to be properly interpreted as a declaration. Section 16.3, “Constraints as Predicate Declarations” explains these requirements in detail.

8.2. Functional Predicates

A predicate can be declared to be functional, i.e., one that represents a function from its key to its value. (In mathematics a function---also known as a mapping or a map---is just a special kind of relation, and a predicate represents a relation.)

A functional predicate contains n-tuples, where n must be at least 1. The first k arguments of a functional predicate are its keys, or key arguments. The last n - k arguments are its values, or value arguments.

If n - k > 1, the predicate is called a multi-valued functional predicate; if n - k = 1, the functional predicate is single-valued. Functional predicates are most often single-valued, and the unqualified term "functional predicate" usually refers to the single-valued case.

If k = 0 (i.e., there is no key), then the functional predicate is effectively a global constant (or, if multi-valued, a tuple of constants). Such a predicate is sometimes called a scalar predicate (or just a scalar).

A functional predicate must satisfy the constraint that no two tuples in the predicate can share the same key (which, as mentioned above, can consist of several arguments). When this constraint is violated, the LogicBlox database reports a functional dependency violation (a.k.a. FDV) and aborts the transaction.

Single-valued functional predicates

The declaration of a single-valued functional predicate takes the following form:

FunctionalPredicateDeclaration =
    FunctionalExpression "=" ValueArgument "->" Atom { "," Atom } "." .

FunctionalExpression = Identifier "[" [ KeyArguments ] "]" .

ValueArgument = Identifier .
KeyArguments  = Identifiers .

Identifiers = Identifier { "," Identifier } .

The key arguments of the predicate must be declared within the square brackets [ and ]. The value argument of the predicate must be declared after the equality (=).

If the key arguments are missing, the declared predicate will be able to contain only one value, i.e., it will effectively be a constant.

Example 8.2. Declaring a functional predicate

In the following example, we declare a functional predicate that maps a person's name to her age. The first two fields in each tuple of this predicate are the key, and the third field is the value associated with the key.

age[given_name, family_name] = age -> string(given_name),
                                      string(family_name),
                                      int(age).

Predicate age can contain tuples such as {("Alice", "Smith", 20), ("Bob", "Jones", 25), ("Alice", "Jones", 20)}. However, given these tuples, an attempt to add ("Alice", "Smith", 40) will cause the LogicBlox database to report an error (functional dependency violation).

Multi-valued functional predicates

The declaration of a multi-valued functional predicate takes the following form:

MultiValuedFunctionalPredicateDeclaration =
    MultiValuedFunctionalAtom "->" Atom { "," Atom } "." .

MultiValuedFunctionalAtom = Identifier "(" KeyArguments ";" ValueArguments ")"
                          | Identifier "(" ";" ValueArguments ")"
                          | Identifier "(" KeyArguments ";"  ")" .

KeyArguments   = Identifiers .
ValueArguments = Identifiers .

Identifiers = Identifier { "," Identifier } .

The semicolon (;) separates the list of key arguments (which may be empty) from the list of value arguments (which may also be empty). The list of key arguments and the list of value arguments cannot both be empty.

Of course, the predicate cannot be called "multi-valued" if the number of value arguments is smaller than two, and we recommend that the syntax be used only for such properly multi-valued predicates.

We mention just for completeness that:

  • It is pointless to use a multi-valued functional atom such as p(x, y ; ) (i.e., with no value arguments), since it is equivalent to the basic atom p(x, y).

  • A declaration such as p( ; x) -> int(x). (i.e., with no key arguments and one value argument), is equivalent to p[] = x -> int(x), which is the recommended form. Such a declaration can be regarded as the declaration of a constant.

  • A multivalued functional atom such as p( ; x, y) (i.e., with no key arguments and more than one value argument) is a way of declaring a predicate that can hold only one tuple. This form is very seldom used.

Note

Frequent unnecessary use of multi-valued functional predicates is considered bad style. However, sometimes such predicates cannot be avoided: see Example 13.2, “Sorting a binary predicate”.

Example 8.3. 

In the following, a social security number is mapped to a person's name and age.

ssn_to_name_age(ssn ; name, age) -> string(ssn), string(name), int(age).

It is usually preferable to have two separate functions:

ssn_to_name[ssn] = name -> string(ssn), string(name).
ssn_to_age[ssn]  = age  -> string(ssn), int(age).

One-to-one functional predicates

A functional predicate can be declared as being one-to-one (i.e., injective). For a predicate named F, the declaration can be given in one of the following two forms:

lang:oneToOne(`F).

lang:isOneToOne[`F] = true.

The declaration causes the compiler to generate constraints that ensure only one combination of key arguments is associated with a given value.

Warning

Before LogicBlox version 4.4.5 such declarations could only be used for functional predicates with only one key argument and one value argument.

Example 8.4. Declaring an injective function

create --unique

addblock <doc>

  F[x, y] = z -> string(x), string(y), string(z).

  lang:oneToOne(`F).
</doc>

exec <doc>
  +F["a", "b"] = "alpha-beta".
  +F["b", "g"] = "beta-gamma".
  +F["a", "B"] = "alpha-beta".    // Not injective!
</doc>
print F

close --destroy

Execution results in failure with an error message:

Error: Constraint failure(s):
block_1Z1FGYMG:0(0)--0(0):
    false <-
      Exists vx2::string,vy2::string,x0::string,x1::string,y0::string,y1::string .
         string:eq_2(vx2,vy2),
         F[x0,x1]=vx2,
         F[y0,y1]=vy2,
         !(
            string:eq_2(x1,y1)
         ).
(1) vx2="alpha-beta",vy2="alpha-beta",x0="a",x1="B",y0="a",y1="b"
(2) vx2="alpha-beta",vy2="alpha-beta",x0="a",x1="b",y0="a",y1="B"

8.3. Entity Predicates

Entity predicates are unary predicates used to represent elements of a program's problem domain. An entity predicate can be thought of as a user-defined type that complements the set of primitive types built into LogiQL: for this reason it is sometimes called an entity type.

For example, instead of declaring a unary predicate state that contains string values, one can declare an entity predicate state. The values of the state entity predicate can be referenced by their string identifiers, yet are distinctly different from the string values of their names, as well as from values of other entity predicates (unless subtyping is used: see Section 8.3.3, “Subtyping”).

The members (values) of an entity predicate are called entities, or entity values. Using entities instead of primitive values such as strings and integers makes a program easier to understand, because the program's data structures correspond better to your own mental model of how the data is arranged. Additionally, using entities helps catch errors such as confusing the state named "Georgia" with a person named "Georgia".

Creating Entities.  Entity values can be created in one of two mutually exclusive ways:

  • First, the entity predicate can be associated with one or more functional constructor predicates. The entities are the values of the function. Adding a tuple to a constructor predicate creates a new entity value in the associated entity predicate.
  • Second, an entity predicate can be associated with a reference-mode predicate (often shortened to refmode predicate). A refmode predicate allows the user to create each entity together with an associated identifier: the identifier can then be used to refer to the entity.

    Note

    Reference mode predicates are now deprecated. It is recommended that new projects use only entities with constructor predicates.

Constructor predicates are described in Section 8.3.1, “Constructor Predicates” below. We defer the discussion of refmode predicates to Section 8.3.2, “Reference-Mode Predicates”.

Declaring An Entity Predicate.  An entity predicate can be declared with or without a reference-mode predicate. The syntax is as follows:

EntityPredicateDeclaration = Identifier "(" Identifier ")" "->" "."
                           | Identifier "(" Identifier ")" ","
                               Identifier "(" Identifier ":" Identifier ")" "->"
                                 Identifier "(" Identifier ")" "." .

Example 8.5.  Entity predicate declarations with and without reference-mode predicates

person(x), person_has_name(x : n) -> string(n).
thing(x) -> .

Entity predicate person is associated with a refmode predicate, person_has_name.

Entity predicate thing is not associated with a refmode predicate.

Optionally, an explicit entity predicate declaration may be included:

ExplicitEntityDeclaration =
         "lang:isEntity" "[" "`" Identifier "]" "=" ("true" | "false") "."
       | "lang:entity" "(" "`" Identifier ")" "." .

With the first form, the predicate is declared to be an entity predicate if the right-hand side of the equality is the value true; otherwise the predicate is explicitly not an entity predicate. With the second form, using lang:entity, the predicate is explicitly declared to be an entity predicate.

Explicit entity predicate declarations are optional for top-level entity predicates (i.e., those that are not subtypes of other entity types). In Section 8.3.3, “Subtyping” we discuss the utility of these explicit declarations.

Example 8.6. 

Below you can find an example where person is explicitly declared as an entity type. Note that there is also a predicate declaration for person in addition to the entity predicate declaration.

person(p) -> .
lang:entity(`person).

Example 8.7. 

Here is an alternative method of declaring person to be an entity predicate:

person(p) -> .
lang:isEntity[`person] = true.

An entity predicate can be either extensional (EDB) or intensional (IDB) (see Section 8.8, “Derivation Types”). For the examples in this chapter we will often use EDB entity predicates, as they are more frequent in practice. (To fully understand the examples, one must first skim through the material in Section 19.1, “Preliminaries” and Section 19.2.1, “Direct manipulation of EDB predicates”.)

8.3.1. Constructor Predicates

A constructor predicate acts as a one-to-one function (injection) that maps a multi-dimensional key to an entity. Key arguments can be either of primitive types or of entity types. The value type of a constructor predicate must be of an entity type (either without a refmode or with an auto-numbered refmode).

Constructor predicates are used primarily to create values for their associated entity predicates. We discuss this in more detail in Section 11.2, “Value-constructing Rules”, but will also see some examples in this section.

To declare a predicate as a constructor predicate we use lang:constructor or lang:isConstructor, as illustrated in the following example.

Example 8.8. Declaration of a constructor predicate

person(p) -> .         // A refmodeless entity type
person_from_names[first_name, last_name] = p
   -> string(first_name), string(last_name), person(p).
lang:constructor(`person_from_names).

Above, the predicate person_from_names is declared to be a constructor predicate. We can use person_from_names to construct new person values, associating each unique first/last name pair with a unique person.

Instead of lang:constructor(`person_from_names) we could have used lang:isConstructor[`person_from_names] = true.

Here is a simple example of how a constructor predicate can be used to populate the associated entity predicate.

Example 8.9. A simple entity predicate with a constructor

create --unique

addblock <doc>

   person(p) -> .

   person:name[first, last] = p -> person(p), string(first), string(last).
   lang:constructor(`person:name).
</doc>

exec <doc>
  +person:name["Betty", "James"] = _.
  +person:name["Harry", "Jones"] = _.
</doc>

echo --- person:
print person
echo --- person:name
print person:name

close --destroy

The bracketed numbers in the output are, roughly, the internal representations of entities within the database. Please note that they are implementation-dependent and cannot be relied on for any purpose.

created workspace 'unique_workspace_2018-12-06-14-44-20'
added block 'block_4LDQPDSB'
--- person:
[10000000006]
[10000000007]
--- person:name
"Betty" "James" [10000000007]
"Harry" "Jones" [10000000006]
deleted workspace 'unique_workspace_2018-12-06-14-44-20'

Some more insight into how to use entities with constructors can be gained from studying Example 15.3, “"Linear recursion" with entities and grouping”.

Structuring Entity Predicates

An entity predicate can be associated with more than one constructor. Each of the constructors maps onto a distinct subset of the entity predicate.

Example 8.10. An entity predicate with several constructors

addblock <doc>

  vehicle(x) -> .

  train[name] = v -> vehicle(v), string(name).
  bus  [name] = v -> vehicle(v), string(name).

  lang:constructor(`train).
  lang:constructor(`bus).
</doc>

exec <doc>
  +train["golden lightning"] = _.
  +train["silver arrow"] = _.

  +bus["silver arrow"] = _.
  +bus["quick fox"] = _.
</doc>

echo --- vehicle:
print vehicle
echo --- train:
print train
echo --- bus:
print bus

The results are:

--- vehicle:
[10000000000]
[10000000001]
[10000000006]
[10000000007]
--- train:
"golden lightning" [10000000006]
"silver arrow"     [10000000000]
--- bus:
"quick fox"    [10000000007]
"silver arrow" [10000000001]

Notice that each constructor maps the same string, "silver arrow", to a different entity.

The multiple constructor predicates can be used as selectors, i.e., to distinguish between the kinds of entities created by different constructors.

Example 8.11. Constructors used as selectors

We can extend Example 8.10, “An entity predicate with several constructors” as follows.

addblock <doc>
   silver(v) -> vehicle(v).

   silver(v) <- v = train["silver arrow"].
   silver(v) <- v = bus  ["silver arrow"].
</doc>

addblock <doc>
  vehicle_description[v] = desc -> vehicle(v), string(desc).

  vehicle_description[v] = "train: " + name  <- v = train[name], silver(v).
  vehicle_description[v] = "bus:   " + name  <- v = bus  [name], silver(v).
</doc>

echo --- description:
print vehicle_description

Notice that silver is just a unary predicate that happens to store some entity values: it is neither an entity predicate nor a constructor predicate.

The output will be

--- description:
[10000000000] "bus:   silver arrow"
[10000000001] "train: silver arrow""

Instead of applying the selectors over and over again, we might prefer to populate separate predicates with the appropriate entities.

addblock <doc>
  trains(v) -> vehicle(v).
  buses (v) -> vehicle(v).
</doc>

exec <doc>
  +train["golden lightning"] = v, +trains(v).
  +train["silver arrow"]     = v, +trains(v).

  +bus["silver arrow"] = v, +buses(v).
  +bus["quick fox"]    = v, +buses(v).
</doc>

echo --- train:
print train
echo --- bus:
print bus
echo --- trains:
print trains
echo --- buses:
print buses
echo --- vehicle:
      print vehicle

In this version of the example the numbers that represent the entities internally have become different (but they are still consistent).

--- train:
"golden lightning" [10000000001]
"silver arrow"     [10000000000]
--- bus:
"quick fox"    [10000000006]
"silver arrow" [10000000003]
--- trains:
[10000000000]
[10000000001]
--- buses:
[10000000003]
[10000000006]
--- vehicle:
[10000000000]
[10000000001]
[10000000003]
[10000000006]

We have just seen how to divide a set of entities into disjoint subsets. Another common way of structuring a set is to order its elements into a hierarchy. This is easily done by means of unary predicates.

addblock <doc>

   person(p) -> .

   person:name[first, last] = p -> person(p), string(first), string(last).
   lang:constructor(`person:name).

   female(p) -> person(p).
   girl(p)   -> person(p).

   lang:isEntity[`female] = false.

   girl(p) -> female(p).
</doc>

The last line in the block is a constraint that requires every item in girl to be also in female. We had to declare that female is not an entity predicate, because otherwise the constraint would be interpreted as a type declaration (see Section 8.3.3, “Subtyping”).

In order to ensure that the constraint is satisfied, we can add the rule:

  +female(p) <- +girl(p).

To add a person to girl we can write simply:

  +person:name["Betty", "James"] = p, +girl(p).

The various aspects of entity predicates are often somewhat hard to integrate in one's mind, so we end this section with a more complicated example.

Example 8.12. A more complicated example with entity predicates

The following block declares two entity predicates product and color. It also declares constructors for these entities and a predicate that associates a product with its color (for the purposes of this simple example we will assume each product has only one color).

addblock <doc>
  product(prod) -> .
  product_id[id] = prod -> string(id), product(prod).
  lang:constructor(`product_id).

  color(c) -> .
  color_id[id] = c -> string(id), color(c).
  lang:constructor(`color_id).

  product_color[prod] = c -> product(prod), color(c).
</doc>

We can populate the predicates as follows (but please see the note at the beginning of Section 19.2.1, “Direct manipulation of EDB predicates”):

exec <doc>
  +color_id["pink"]  = c, +product_id["Barbie"] = p, +product_color[p] = c.
  +color_id["black"] = c, +product_id["Darth"]  = p, +product_color[p] = c.
  // ...
</doc>

We will now extend our database with information about features of the merchandise. One important kind of feature is color, another may be the department in which an item is sold. The various kinds of features can be thought of as disjoint subsets of the set of entities of type Feature.

(Since we are in a prototyping phase, we represent a department simply by its name: in a full-blown application it would be an entity.)

addblock <doc>
  Feature(_) -> .

  colorFeature[c] = f -> color(c), Feature(f).
  lang:constructor(`colorFeature).

  departmentFeature[dept] = f -> string(dept), Feature(f).
  lang:constructor(`departmentFeature).
</doc>

Finally, we declare a predicate that associates each feature with its description, which is a string. We then write the rules that populate this predicate.

The body of the first rule retrieves the colors of all the products in the database, extracts their names from the constructor predicate color_id and creates strings that are the desired descriptions of the associated features. The head of the rule generates the features asssociated with the retrieved colors and associates them with the appropriate descriptions.

The second rule is similar, but simpler, because we are still only prototyping everything that has to do with departments.

addblock <doc>
  featureDescription[f] = description -> Feature(f), string(description).

  featureDescription[f] = desc,
  colorFeature[c] = f
      <-  desc = "product_color: " + id,
          color_id[id] = c,
          product_color[_] = c.

  featureDescription[f] = desc,
  departmentFeature[dept] = f
      <-  desc = "department: " + dept,
          (dept = "toys"; dept = "lingerie").

</doc>

We can now print the feature descriptions:

    print featureDescription

The result is shown below.

[10000000012] "department: toys"
[10000000013] "department: lingerie"
[10000000014] "product_color: black"
[10000000015] "product_color: pink" 

8.3.2. Reference-Mode Predicates

Note

Reference mode predicates are now deprecated. It is recommended that new projects use only entities with constructor predicates (see Section 8.3.1, “Constructor Predicates”).

A reference-mode predicate, or refmode for short, is a binary predicate that must be associated with a top-level entity predicate. Specifically, each tuple in a refmode predicate relates a value of a primitive type to exactly one entity. The primitive value thus provides the programmer with a way to identify and refer to an entity value.

The refmode predicate must be declared together with its entity predicate. Furthermore, for every value of that entity predicate, there must be a reference-mode value that uniquely identifies the entity value.

Example 8.13. Declaring an entity predicate with refmode

Entity predicate person in this example is declared to have the refmode predicate person_has_name, and a refmode value of type string. That is, person values can be identified using their string-valued names.

person(p), person_has_name(p : n) -> string(n).

An entity predicate can have only one associated refmode predicate. If an entity predicate has a refmode predicate, then its values are constructed along with the associated refmode values.

The construction of values in rules, and the role of refmode predicates in it, are described in Section 11.2, “Value-constructing Rules”. It is also possible to construct entity values by explicitly adding facts, as shown in the example below.

Example 8.14. Constructing an entity by adding a fact

Given the declarations in Example 8.13, “Declaring an entity predicate with refmode” we can create a new person entity with the associated name "Bob" by executing the code shown below (the word "executing" was carefully chosen: see Chapter 19, Transaction Logic).

+person(p), +person_has_name(p : "Bob").

It is recommended that this be written in the functional style:

+person(p), +person_has_name[p] = "Bob".

We can even omit the explicit mention of person and write just

+person_has_name[_] = "Bob".

Auto-Numbered Refmode Predicates

Note

Just like "normal" refmode predicates, auto-numbered refmode predicates are now deprecated. If you require automatically-generated unique numbers, please see Section 7.11, “Unique Identifiers”.

Refmodes values may be automatically created by the system. This is a useful feature when the exact values of refmodes do not matter, as long as they are different from one another. The value type of an auto-numbered refmode must be int.

Example 8.15. 

The following example declares entity predicate q and its auto-numbered refmode predicate q_id:

q(x), q_id(x:i) -> int(i).
lang:autoNumbered(`q_id).

Auto numbered entity types can be populated by associating them with constructors, or by populating their subtypes.

Example 8.16. Populating an auto-numbered predicate

The following lb script (see Section 19.1, “Preliminaries”) uses constructor predicates (see Section 8.3.1, “Constructor Predicates”) to populate person, which is an entity predicate with an auto-numbered refmode predicate.

(Note that we use name both as the name of a predicate and the name of a variable to show that this is allowed, but such usage is not necessarily recommended.)

create --unique

addblock <doc>
  person(p), person_id(p : i) -> int(i).
  lang:autoNumbered(`person_id).

  name[name] = c -> string(name), person(c).
  lang:constructor(`name).
</doc>

exec <doc>
  +name["Betty"] = _.
  +name["Bob"] = _.
  +name["Betty"] = _.
</doc>
echo --- person:
print person
echo --- name:
print name
echo --- person_id:
print person_id

addblock <doc>
  person_name_id(name, id) <- person_id(p : id), name[name] = p.
</doc>
echo --- person_name_id
print person_name_id

close --destroy

Here is the output (whose exact form may change between versions of the system):

created workspace 'unique_workspace_2017-03-28-23-28-23'
added block 'block_1Z1F0KWV'
--- person:
[10000000004] 10000000004
[10000000005] 10000000005
--- name:
"Betty" [10000000005] 10000000005
"Bob"   [10000000004] 10000000004
--- person_id:
[10000000004] 10000000004 10000000004
[10000000005] 10000000005 10000000005
added block 'block_1Z1O5ERS'
--- person_name_id
"Betty" 10000000005
"Bob"   10000000004
deleted workspace 'unique_workspace_2017-03-28-23-28-23'

Each tuple of person contains an internal identifier of the entity (shown in square brackets) and the associated refmode value. In the example these numbers happen to be identical.

Each tuple in name contains the name and information about the associated entity (i.e., person).

Each tuple in person_id contains information about the entity and the (duplicated) auto-generated number.

The tuples in person_name_id pair names of entities with the the auto-generated numbers.

Notice that the repetition of +name["Betty"] = _. did not result in producing a new entity.

Example 8.17. Populating an auto-numbered predicate via its subtypes

The following lb script (see Section 19.1, “Preliminaries”) uses constructor predicates (see Section 8.3.1, “Constructor Predicates”) to populate subtypes of person, which is an entity predicate with an auto-numbered refmode predicate. (See Section 8.3.3, “Subtyping” for a discussion of subtypes.)

create --unique

addblock <doc>
  person(p), person_id(p : i) -> int(i).
  lang:autoNumbered(`person_id).

  child(c) -> person(c).
  adult(a) -> person(a).

  diminutive[name] = c -> string(name), child(c).
  lang:constructor(`diminutive).

  official[name] = a -> string(name), adult(a).
  lang:constructor(`official).
</doc>

exec <doc>
  +diminutive["Betty"] = _.
  +diminutive["Bob"] = _.

  +official["Elizabeth"] = _.
  +official["Robert"] = _.
  +official["Bob"] = _.
</doc>
echo --- child:
print child
echo --- adult:
print adult
echo --- person:
print person
echo --- diminutive:
print diminutive
echo --- official:
print official
echo --- person_id:
print person_id

close --destroy

The output is shown below. See the example above for an explanation.

created workspace 'unique_workspace_2017-03-28-21-33-33'
added block 'block_1Z1F0LVQ'
--- child:
[10000000005]
[10000000007]
--- adult:
[10000000001]
[10000000004]
[10000000006]
--- person:
[10000000001] 10000000001
[10000000004] 10000000004
[10000000005] 10000000005
[10000000006] 10000000006
[10000000007] 10000000007
--- diminutive:
"Betty" [10000000005]
"Bob"   [10000000007]
--- official:
"Bob"       [10000000004]
"Elizabeth" [10000000006]
"Robert"    [10000000001]
--- person_id:
[10000000001] 10000000001 10000000001
[10000000004] 10000000004 10000000004
[10000000005] 10000000005 10000000005
[10000000006] 10000000006 10000000006
[10000000007] 10000000007 10000000007
deleted workspace 'unique_workspace_2017-03-28-21-33-33'

It is worth noticing that the use of "Bob" in child and the use of "Bob" in adult produce two different entities in person.

8.3.3. Subtyping

Note

Subtyping is now deprecated (starting with LogicBlox 4.9). See the section called “Structuring Entity Predicates” for examples of how to replace the most common uses of this feature.

An entity predicate can be declared to be the subtype of some parent entity predicate. The subtyping relationship between entity predicates forms a tree: subtyping relationships cannot form a cycle, nor can an entity predicate have multiple parent types.

The values of an entity subtype and the values of its supertype form a subset relationship: a value of a subtype entity predicate is also a value of the supertype entity predicate. Two entity subtypes of the same supertype may, but need not, contain the same values.

Explicit Subtype Declaration.  An explicit subtype declaration contains two parts: a subsetting constraint, and an explicit entity predicate declaration. The subsetting constraint has the following form:

SubtypeDeclaration = Identifier "(" Identifier ")" "->"
                        Identifier "(" Identifier ")" "." .

Example 8.18. Declaring entity subtypes

The following example explicitly declares female to be a subtype of person. We assume that person has previously been declared as an entity predicate.

female(x) -> person(x).
lang:entity(`female).

Alternatively, replacing the second line with lang:isEntity[`female]=true. would achieve the same effect.

Implicit Subtype Declaration.  A unary predicate T may be implicitly declared to be a entity subtype if it satisfies both of the following conditions:

  • There exists a constraint T(x) -> E(x)., where E is an entity predicate (possibly an entity subtype).
  • T is used as an entity type in a predicate declaration. That is, there exists a predicate declaration constraint where T appears on the right-hand-side as a type. For example,
    P(... x ...) -> ... T(x) ... .

Example 8.19. An implicit subtype declaration

The following example implicitly declares female to be an entity predicate, and that it is a subtype of person. Predicate girl is not considered an entity predicate. (See Example 8.23, “ A true subtype vs. a predicate that only looks like one ” for more information about this example.)

female(x) -> person(x).
girl(x) -> female(x).

Example 8.20. Another implicit subtype declaration

In this example, female is used, in the second line, as a type in the declaration of predicate person_hasmother, and thus is implicitly declared as an entity predicate.

female(x) -> person(x).
person_hasmother[p] = m -> person(p), female(m).

An implicit subtype declaration can be explicitly overridden with lang:isEntity[`T] = false.

It is a compile-time error for a program to include two entity predicate declarations for the same predicate with different supertypes -- unless the two supertypes are in a subtype relationship with each other.

Example 8.21. An entity type with two supertypes

The following declarations will be rejected as invalid, since female cannot be a subset of both person and car.

person(x) -> .
car(x) -> .
female(x) -> person(x).
female(x) -> car(x).

However, the following declarations are valid, since female and person are declared to be in a subtype relationship.

person(x) -> .
female(x) -> person(x).

girl(x) -> female(x).
girl(x) -> person(x).
lang:isEntity[`girl] = true.

It is also a compile-time error for an entity predicate to be declared both as a top-level entity type and the subtype of another top-level entity type.

Example 8.22.  Entity predicate declared to be both a top entity type and a subtype

The following declarations will be rejected as invalid by the compiler:

person(x) -> .
person(x) -> thing(x).

Example 8.23.  A true subtype vs. a predicate that only looks like one

The following is an expanded version of Example 8.19, “An implicit subtype declaration”.

addblock <doc>
  person(x) -> .
  girl(x)   -> person(x).

  name[nm] = p -> string(nm), person(p).
  lang:constructor(`name).

  name["Betty"] = p, girl(p).
</doc>
echo --- person:
print person
echo --- girl:
print girl

This would print out

--- person:
[10000000004]
--- girl:
[10000000004]

As we see, girl can contain elements of type person. However, it is not a subtype of person. This can be demonstrated by extending the program with

addblock <doc>
  foo[x] = v -> person(x), int(v).
  foo[x] = v -> girl(x), int(v).

  name["Betty"] = p, foo[p] = 7.
  name["Bob"]   = p, foo[p] = 8.
</doc>

An attempt to execute would result in

Error: Constraint failure(s):
block_4LDQPDUZ:2(3)--2(31):
    false <-
      Exists v::int,x::person .
         foo[x]=v,
         !(
            girl(x)
         ).
(1) v=8,x=[10000000006]

What happen here is that foo[x] = v -> girl(x), int(v). is treated as a constraint that requires keys of foo to be elements of girl, and the entity that is constructed by "Bob" violates the constraint.

If, however, we ensured that girl is a constructor, for example by changing the declarations to

  person(x) -> .
  girl(x)   -> person(x).
  lang:entity(`girl).

then the program would not fail, because the second line in

  foo[x] = v -> person(x), int(v).
  foo[x] = v -> girl(x), int(v).

would be treated as just a type declaration (redundant, but compatible with the first one).

It may be somewhat surprising that if we remove the first line above (i.e., the key of foo is declared as being just of type girl), then the program will still be correct: the entity that represents the person whose name is "Bob" does not belong to type girl, but belongs to its supertype, so the type checker does not complain.

When in doubt about the status of a predicate, you can run the lb command predinfo, for example

predinfo girl

and look for the value of field is_entity.

8.4. Constructor predicates

Constructor predicates are closely related to entity predicates. We discuss them in Section 8.3.1, “Constructor Predicates”.

8.5. Reference mode (refmode) predicates

Refmode predicates are closely related to entity predicates. Their functionality is similar to constructor predicates, and they are now deprecated. We discuss them in Section 8.3.2, “Reference-Mode Predicates”.

8.6. Foreign Predicates

Note

This section requires some understanding of branches. See Chapter 41, Database Branching.

Transaction-lifetime rules (see Chapter 19, Transaction Logic) may refer to predicates that exist in both the current branch and a foreign branch. This is done by writing P@branchname for a predicate P and a branch named branchname. Writing P without the @branchname tag refers to the predicate P that resides in the branch associated with the current transaction (e.g., NAME in transaction --branch NAME), or the open workspace's current default branch if the branch is unspecified. The signatures on both branches must match, otherwise a runtime error will be raised.

When the foreign predicate has an entity type in its signature, all uses of that entity type must be "guarded" by a use in the current branch. Entities are local to a single path of a branch's history, so any split in history creates distinct entities for the same application of a constructor to some previously unseen arguments. The guardedness restriction ensures entities stay local to a branch.

Example 8.24. Guardedness of foreign entities

Assume the person_from_names example from Section 8.3.1, “Constructor Predicates”. The use of person is a "guard" for the use of entity p in the following rule:

+same_here(p) <- person_from_names@foreign[_, _] = p, person(p).

Notice that same_here will be populated only by those entities of type person that (1) were present in this branch (or its ancestor) before the foreign branch diverged from it, and (2) are still present in the foreign branch when the rule is evaluated.

Example 8.25. Reconstructing foreign entities

The lb script below shows that it is possible to construct entities from the values of constructor predicates in a different branch. However, the entities on the two branches will be different.

create --unique

addblock <doc>
  person(p) -> .
  person_from_names[first_name, last_name] = p
     -> string(first_name), string(last_name), person(p).
  lang:constructor(`person_from_names).
</doc>

branch foreign

addblock --branch foreign <doc>

  person(p), person_from_names[fn, ln] = p <- first_name(fn), last_name(ln).

  first_name("John").
  last_name("Smith").

  first_name("Mary").
  last_name("Jones").
</doc>

exec <doc>
  +person(p),
  +person_from_names[fn, lm] = p <- person_from_names@foreign[fn, lm] = _.
</doc>

print person
print person_from_names

close --destroy

Results:

created workspace 'unique_workspace_2016-07-23-01-28-59'
added block 'block_1Z1C3B1V'
added block 'block_1Z1EX11X'
[10000000000]
[10000000001]
[10000000002]
[10000000003]
"John" "Jones" [10000000001]
"John" "Smith" [10000000000]
"Mary" "Jones" [10000000003]
"Mary" "Smith" [10000000002]
deleted workspace 'unique_workspace_2016-07-23-01-28-59'

Please note that this simple scheme works only if the arguments of the foreign constructor predicate are of primitive types (i.e., are not entities).

8.7. File Predicates

Note

File predicates should not be used except for implementing very low level functionalities. Most programmers should use Tabular Data Exchange services (see Section 27.1, “Configuring Tabular Data Exchange Services”).

File predicates are one means by which a program can perform I/O. These predicates are used to treat an external file as a predicate: if a predicate is declared to be a file predicate, then the tuples of this predicate correspond to the contents of the file. File predicates can be used inside exec blocks (including queries) and inactive blocks (including pre-compiled queries).

As a simple example, consider the following logic, which copies records from an input file to an output file, incrementing each number that it encounters.

Example 8.26. Simple input/output using file predicates

_in(offset; s, x) -> int(offset), string(s), int(x).
lang:physical:filePath[`_in] = "input.csv".
lang:physical:fileMode[`_in] = "import".

_out(s, x) -> string(s), int(x).
lang:physical:filePath[`_out] = "output.csv".
lang:physical:fileMode[`_out] = "export".

_out(s, y) <- _in(_; s, x), y = x + 1.

In this example, the input file input.csv is represented by the predicate _in, which has three attributes: an integer key argument offset representing the physical position of a line in the file, followed by two value attributes (of types string and int, respectively) corresponding to the first and second column in the file. The output file output.csv is represented by the predicate _out.

lang:physical:filePath[`_in] and lang:physical:filePath[`_out] are used to specify that the predicates _in and _out are file predicates, and to tell the system where to find the corresponding file. lang:physical:fileMode[`_in] and lang:physical:fileMode[`_out] are used to specify whether a file predicate is used for import or for export.

In the above example, if input.csv contains the following data:

John,43
Mary,25
Bill,14

then, after executing this logic, the file output.csv will contain:

John,44
Mary,26
Bill,15

Import file predicates must have a signature that includes an integer offset attribute, separated from the other attributes by a semicolon; export file predicates do not have an offset argument. All attributes of file predicates must have primitive (non-entity) types. The conversion from values in the file to the specified primitive types happens automatically.

As a second example, the following logic defines a small schema to model persons, and imports the contents of file input.csv to represent persons and their names.

Example 8.27. 

addblock '
   person(p), person_has_name(p:n) -> string(n).
'

exec '
   _in(offset; s, i) -> int(offset), string(s), string(i).
   lang:physical:filePath[`_in] = "input.csv".
   lang:physical:fileMode[`_in] = "import".

   +person(p), +person_has_name[p] = s <- _in(_; s, _).
'

The files accessible by file predicates are known as delimited files. These are text files, where each line is a sequence of fields separated by a delimiter. A common example of a delimited file is a csv (comma-separated values) file used by spreadsheet programs as exported text versions of spreadsheet content. In the case of csv files, the comma character (",") is the delimiter. The comma character is also the default delimiter used by file predicates, but other characters can be configured using lang:physical:delimiter. Note that choosing the newline character ("\n") as the delimiter has the effect that each line of the file is treated as a record consisting of a single field.

File predicate settings (values must be literal strings, not variables).
lang:physical:filePath Path to the file (required).
lang:physical:fileMode One of "import", "export". If not specified, "import" is used by default.
lang:physical:delimiter Character used as delimiter. If not specified, "," is used by default.
lang:physical:columnNames Comma-separated list of column names. If set, the first line of the file is treated as a header, and the columns of the file are identified by their column name in the header, rather than by their order of occurrence.
lang:physical:hasColumnNames If set to true (or if lang:physical:columnNames is specified), the first line of the file is treated as a header, rather than as a record.

When using lang:physical:columnNames, it is possible to specify optional columns by using square brackets (as in lang:physical:columnNames[`_in]="item,price,[discount]"). Optional columns must be of type string. When an optional column is absent in the file, the empty string "" is used as a default value. Specifying a column as being optional has no effect on file export.

On file import, when a record has the wrong number of fields, or a value cannot be successfully converted to the specified primitive type, an error message is emitted and the transaction is aborted.

8.8. Derivation Types

Every predicate has a derivation type associated with it. This can be extensional, intensional, or derived-only. The derivation type of a predicate indicates how it receives its values, and whether its contents are maintained or materialized (cached).

Extensional predicate.  An extensional predicate is most frequently referred to as an EDB predicate. It stores values of the extensional database, that is, values that are inputs to the database. These values exist because the user explicitly imported them into the database. EDB predicates are populated through data imports, or through direct manipulation of the predicate via "delta logic" (see Section 19.2, “Delta logic”). Data in extensional predicates, once removed, cannot be recovered (unless there is a back-up copy of the data).

EDB predicates are also used to store event data, and to trigger other events (Section 19.3, “Events”).

Intensional predicates: Derived or DerivedAndStored.  A "DerivedAndStored" predicate is most frequently referred to as an IDB or intensional predicate. Its values are computed via IDB rules (Chapter 11, Rules) -- effectively, logical implications that specify what values the predicate should contain, based on the values of other EDB or IDB predicates. As the values of IDB predicates are computed from rules, their values can always be recovered from the EDB predicates. Their values are also automatically maintained by the database so that the logical implications defining them always hold. A "Derived" (also known as Derived-only) predicate is a refinement of the above. It specifies not only that the content of a predicate is computed, but also that its contents are not materialized (or cached). Derived-only is explained in more detail in Section 11.3, “Derived-only Rules”.

A predicate's derivation type can be explicitly declared using the following form:

DerivationTypeDeclaration =
  'lang:derivationType' '[' '`' Identifier ']' '=' '"' DerivationType '"' '.' .
DerivationType = 'Extensional' | 'DerivedAndStored'
               | 'Derived'     | 'NotDerived'
               | 'IntegrityConstraint'.

A predicate's derivation type can also be inferred. The types that are inferrable are extensional and intensional. A derived-only derivation type must be explicitly declared.

A predicate is inferred to be an IDB if there is an IDB rule that derives into it (Section 11.1, “Basics of IDB Rules”). It is inferred to be an EDB if there is a delta rule (a.k.a. EDB rule) that derives into it (Section 19.2, “Delta logic”). If there is no rule that derives into it at all, then it is treated as an EDB predicate, until a rule is added to make its derivation type IDB.

Example 8.28. Predicate derivation type inference

If a predicate P is used with a delta rule
+P(x) <- H(x), x < 20.
then LogiQL infers that P must have an "Extensional" derivation type.

Example 8.29. Explicitly declaring predicates with derivation types

p(x, y) -> int(x), int(y).
lang:derivationType[`p] = "Extensional".

q(x) -> .
lang:derivationType[`q] = "DerivedAndStored".

r(x, y) -> string(x), q(x).
lang:derivationType[`r] = "Derived".

A predicate's derivation type is "NotDerived" by default until declared or inferred. The "IntegrityConstraint" derivation type is not used and will be removed in the future.

8.9. Ordered Predicates

An entity predicate captures a set of values. It is often useful to display those values in a particular order (e.g., when creating reports). For instance, entities representing months should be ordered as January, February, March, etc.

LogicBlox supports a simple programming idiom for such cases: the predicate e_next defines an ordering for entity e. This support takes two main forms:

Using either rules or tool support, the user is responsible for inserting tuples into the e_next predicate to specify the desired ordering.

Example 8.30. 

The following logic orders month-entities in the natural way:

month(_) -> .
mkMonth[s] = m -> string(s), month(m).

lang:entity(`month).
lang:ordered(`month).
lang:constructor(`mkMonth).

month(jan),
month(feb),
month(mar),

mkMonth["January" ] = jan,
mkMonth["February"] = feb,
mkMonth["March"   ] = mar,

month_next[jan] = feb,  // Establish an order on months
month_next[feb] = mar
  <- .

// Examples of generated predicates.
-> month_first[] = mkMonth["January"].
-> month_last[]  = mkMonth["March"].
mkMonth["March"] = mar -> month_prev[mar] = mkMonth["February"].

The declaration lang:ordered(`e) has the same meaning as the following block:

// Predicate declarations
e_first[]  = n -> e(n).
e_last[]   = n -> e(n).
e_prev[n1] = n2 -> e(n1), e(n2).
e_next[n1] = n2 -> e(n1), e(n2).

// First, last, prev defined automatically from next
e_first[]  = n <- e(n), !(e_next[] = n).
e_last[]   = n <- e(n), !(e_next[n] = _).
e_prev[n2] = n1 <- e_next[n1] = n2.

// Constraints
e(_) -> e_first[] = _.
e(_) -> e_last[] = _.

When modular logic is used, the lang:ordered declaration may be placed either in a module's exports section (thus publicly declaring the predicates) or in the clauses section (for private declarations). See Chapter 21, Modules.

8.10. Local predicates

The term local predicate refers to a predicate whose name begins with an underscore (_). The predicate is local to the block in which it is declared. We mention this here for completeness, but see Section 19.1.3, “Blocks” for an explanation (and Example 19.1, “A command or query cannot declare a non-local predicate” for an example).

8.11. External Diff Predicates

During the course of a transaction the contents of a predicate may undergo various changes (Section 19.4, “Stages”). A predicate may also have different versions on different branches (Chapter 41, Database Branching). An external diff (or ediff ) predicate is a system-generated predicate that is associated with an arbitrary "normal" predicate p and that contains the difference between two versions of p. (If the versions are not different, the predicate is empty.)

An ediff predicate is materialised and populated only if it is used; it has transaction lifetime (Section 19.1.5, “The notion of "lifetime"”), and is computed at stage INITIAL (Section 19.4.2, “The Six Stages”).

See Section 10.1.5, “External diff atoms” for information about how to use (and---as a side effect---create) external diff predicates. (See also Section 9.4, “Function applications”).

For example, the contents of the external diff predicate (p@prev \ p@Branch)(x, y) is the set difference between the contens of p@prev and the contents of of p@Branch. One can think of this predicate as if were defined by the (syntactically illegal) rule:

(p@prev \ p@Branch)(x, y) <-  p@prev(x, y),  ! p@Branch(x, y).

The actual computation, however, is much more efficient than the evaluation of such a rule. Moreover, if the flipped version of the ediff predicate ((p@Branch \ p@Bprev) in our example) is also needed, then the two are computed simultaneously, at a cost that is comparable to computing only one of them.

Example 8.31. Computing and using external diff predicates

In the example below there are three invocations of three ediff predicates. For example, (foo@BE\foo@prev)(x) refers to the difference between the current contents of foo in branch BE and the contents of foo in the current branch before the transaction started.

create --unique

addblock <doc>
  foo[x] = y -> int(x), string(y).
  fun[x] = y -> int(x), string(y).
  fie[x] = y -> int(x), string(y).
  bar[x] = y -> int(x), string(y).
  baz[x] = y -> int(x), string(y).

  expected_fie[x] = y -> int(x), string(y).
  expected_bar[x] = y -> int(x), string(y).
  expected_baz[x] = y -> int(x), string(y).
</doc>

exec <doc>
  +foo[1] = "a".  +foo[2] = "b".
</doc>

branch BE

exec --branch BE <doc>
  +foo[11] = "A".  +foo[22] = "B".
</doc>


exec <doc>
  +foo[3] = "a".  +foo[11] = "A".

  +fun[x] = y <- foo@BE[x] = y.
  +fie[x] = y <- (foo@BE\foo@prev)[x] = y.
  +bar[x] = y <- (foo\foo@BE)[x] = y.
  +baz[x] = y <- (foo@BE\foo)[x] = y.
</doc>
echo foo:
print foo
echo fun:
print fun
echo fie:
print fie
echo bar:
print bar
echo baz:
print baz

close --destroy 

The result:

created workspace 'unique_workspace_2016-12-01-17-22-46'
added block 'block_1Z1B36YP'
foo:
1  "a"
2  "b"
3  "a"
11 "A"
fun:
1  "a"
2  "b"
11 "A"
22 "B"
fie:
11 "A"
22 "B"
bar:
3 "a"
baz:
22 "B"
deleted workspace 'unique_workspace_2016-12-01-17-22-46'

8.12. Predicate Properties

In the previous sections, we introduced property declarations that indicate whether a predicate is an entity, a constructor, or an auto-numbered refmode. Some additional predicate properties are explained in this section.

Predicate properties can be declared by using the following syntax:

pred_property_decl = true_property "(" "`" Identifier ")" "."
                   | bool_property "[" "`" Identifier "]" "="
                        ("true" | "false") "." .

true_property      = "lang:autoNumbered"
                   | "lang:constructor"
                   | "lang:derivationType"
                   | "lang:entity"
                   | "lang:ordered"
                   | "lang:pulse"
                   | "lang:oneToOne" .

bool_property      = "lang:isAutoNumbered"
                   | "lang:isConstructor"
                   | "lang:isEntity"
                   | "lang:isPulse"
                   | "lang:isOneToOne" .

Predicate properties must be declared at the same time as a predicate is declared. Once set, a property cannot be changed without rebuilding the workspace (and thus re-declaring the predicate).

Identifier indicates the predicate for which the property is being set.

These properties are explained in other sections of this manual: