fizz

Jean-Louis Villecroze
jlv@f1zz.org @CocoaGeek
August 30, 2018     (Version 0.4.0-X)
Abstract

fizz is an experimental language and runtime environment for the exploration of cognitive architectures and combined Machine Learning (ML) and Machine Reasoning (MR) solutions. It is based primarily on symbolic programming and fuzzy formal logic, and it features a distributed, concurrent, asynchronous and responsive inference engine.

1 About this document

This document is a user manual for fizz and assumes some basic familiarity with logic programming. It is divided into the following parts:

Concepts & Syntax introduces the concepts and the syntax used to describe and manipulate knowledge
Console introduces the usage of the builtin console
Terms introduces the various types that can be manipulated
Primitives lists and describes all the primitives functions
Elementals lists and describes all the supported Classes of Elementals
Advanced topics describes more advanced topics including the Services
Release notes contains pertinent information for each subsequent releases

All code elements are presented in a distinct font like print("hello, world!"). Note that any tabulation shown in a listing is only present to enhance the readability of the code. Tabulations are not part of the language syntax. Primitives syntax is often a combination of code element and italic font. The part in italic is always the input to the primitive. Primitives inputs use special symbols :

symbol? indicates that the input is optional
symbol|number indicates that the input can be either a symbol or a number
symbol+ indicates that the primitive can take on several symbols as input,
but at least one is required
symbol* indicates that the primitive can take on several symbols as input,
but one is optional

Many thanks to Joshua Nozzi (@JoshuaNozzi) and Keith Kolmos (@KeithMKolmos) for reviewing this document and providing many insightful corrections and suggestions.

2 Concepts & Syntax

If you are familiar with PROLOG, you will find that fizz takes some of its fundamental elements and syntax from it. There are five main concepts in fizz , which we will be discussing in this section:

Knowledge is a collection of related statements and/or prototypes.
Statement is a collection of terms with an assigned truth value (think fact).
Predicate is a labeled collection of terms with an assigned truth value range (or variable).
Prototype is a chained collection of predicates that can be evaluated (think rule).
Elemental is a runtime object which hold knowledge and can answer to query.
Service is a runtime object which provide a unique service within the runtime.

One of the main differences between PROLOG and fizz is how inference is done not by a single entity having access to all facts and rules, but by the cooperation of a collection of objects each having access only to what they must know (knowledges). Elemental objects in fizz are very much independent actors, which must exchange messages (mostly by a queries and replies mechanism) in order to execute any inferences. While this is far from being the most efficient method (and performance in some aspect is much worse for some types of inferences) it allows for instance a statement that is broadcasted to trigger the execution of any prototype that references it (via a predicate). It also supports inferences to be distributed among many cores and/or many hosts11not yet fully implemented.

2.1 Knowledge

A Knowledge groups a series of related Statements and Prototypes under the same logical concept (often refered in this document as ”label”). For example, if we wanted to create a list of the three basic colors we would define it as follows:

1color {
2
3    (red,1.0,0.0,0.0);
4    (green,0.0,1.0,0.0);
5    (blue,0.0,0.0,1.0);
6
7}

Knowledge definition always starts with a label that identifies the concept, followed by a frame (optional) and a series of Statements and/or Prototypes within curly brackets. The frame (see Section 3.3 for details on that term) specified after the symbol is known as the properties of the knowledge.

When a knowledge is used to define only statements, it is said to be factual knowledge. If it contains only prototypes, it is called a procedural knowledge.

2.2 Statement

A Statement, as we have seen in the example above, is a comma-separated list of terms within parantheses and terminated by a semicolon. We will look into all the supported terms in more details in Section 3, but so far we have used symbols and numbers. Each time a statement is defined, it can be assigned a truth value (indicating the relation of the statement to truth). Let’s look at an example where each statement is assigned a value to represent the likelihood of a given weather occurence in a particular city:

1weather {
2
3    (paris,rain)        := 0.8;
4    (seattle,sunny)     := 0.2;
5    (london,fog)        := 0.9;
6    (mawsynram,rain)    := 1;
7    (honolulu,snow)     := 0;
8    (honolulu,rain)     := 0.1;
9    (honolulu,sunny)    := 0.6;
10    (honolulu,cloudy)   := 0.3;
11
12}

It’s so unlikely that you will see snow in Honolulu, that we here state that such statement is false.

A truth value is always a number between 0 (false) and 1 (true). When no truth value is assigned, the default value for a statement is 1. It is always defined last, prefixed with a :=. As part of a statement definition, we could also join a collection of properties that apply to the statement in the form of a frame object which is inserted right after the closing parenthesis. Here’s a version of the above knowledge where each statement have been timestamped (see section 5.2 for how):

1weather {
2
3    (paris,rain)        {stamp = 1507093154.766867} := 0.8;
4    (seattle,sunny)     {stamp = 1507093158.846844} := 0.2;
5    (london,fog)        {stamp = 1507093174.863446} := 0.9;
6    (mawsynram,rain)    {stamp = 1507093176.743262} := 1  ;
7    (honolulu,snow)     {stamp = 1507093177.671228} := 0  ;
8    (honolulu,rain)     {stamp = 1507093178.743266} := 0.1;
9    (honolulu,sunny)    {stamp = 1507093179.807307} := 0.6;
10    (honolulu,cloudy)   {stamp = 1507093180.879415} := 0.3;
11
12}

Without getting ahead of ourselves (next section), a statement’s properties can be queried the same way as its terms:

?- #weather(:x,:y) {stamp = :s?[gte(1507093176)]}
-> ( mawsynram , rain , 1507093176.743262 ) := 1.00 (0.001) 1
-> ( honolulu , rain , 1507093178.743266 ) := 0.10 (0.002) 2
-> ( honolulu , sunny , 1507093179.807307 ) := 0.60 (0.002) 3
-> ( honolulu , cloudy , 1507093180.879415 ) := 0.30 (0.002) 4

2.3 Predicate

A Predicate, while being syntactically similar to a Statement, represents not a fact but a question to be figured out. In the following example we will write a predicate which formulates the query: ”tell me where it is very likely to rain”:

1@weather(:x,rain) <0.7|1.0>

The <0.7|1.0> at the end of the predicate is a truth value range. In this case, it indicates that we will only accept the statements where truth values are between 0.7 and 1.0. Beside a range, a predicate will also accept a number or an unbound variable. The latter will allow the truth value of each statements received for the predicate to be used in the following predicates.

Because a predicate is querying a particular knowledge, its label must be indicated. Here, we’re using the weather knowledge we defined earlier. The @ prefix indicates to the runtime that the predicate is referencing a knowledge and not a primitive. Primitives are built-in functions, such as lst.length, which can be used to get the number of elements in a list term. See Section 5 for all the supported primitives. If we wanted to use a primitive we would have omitted the @ like in this example:

1lst.length([1,2,3,4,5],:length)

There is however a situation when a prefix (other than !) can be used with a primitive. Using & will cause the primitive to be executed on the runtime environement threads pool and not within the elemental. We will often reference this as ”offloading”.

A secondary meaning of the @ prefix is to indicate that the predicate should be considered a trigger. As stated in section 2), when a statement is broadcasted in the runtime environment, the predicate will set up the prototype to which it belongs for evaluation. For performance reasons, it is often best to indicate when a given predicate is not a trigger. For these situations, the @ prefix can be replaced by #. If we look back at our earlier example, any new weather statement will activate the prototype in which we used that predicate, we can change it as follow:

1#weather(:x,rain) <0.7|1.0>

Lastly, if a caret (^) is added right after the terms of the predicate, it will indicate that once the predicate as succeded, the solver should not consider any other alternative based on any of the predicates that came before (this is similar to the cut operator in PROLOG). When the predicate is part of series of prototypes, the other prototypes may still be considered depending on what type of predicates came before the cut. To illustrate a cut let’s consider the following example which defines str.default as a knowledge which given a term will either ”return” that term when it is a valid string or a second term if it is not:

1str.default {
2
3    (:a,:b,:b) :- console.puts("1>"), !is.string(:a)^;
4    (:a,:b,:b) :- console.puts("2>"), is.string(:a) , str.length(:a,0)^;
5    (:a,:b,:a) :- console.puts("3>"), is.string(:a) , str.length(:a,_?[gt(0)]);
6
7}

If we now query this knowledge with a symbol as first term, we would expect the second term to be unified with the third term:

?- #str.default(a,"b",:b)
1>
-> ( "b" ) := 1.00 (0.001) 1

As we have started each prototypes with a call to the console.puts primitive, we can observe how the second and third prototypes were indeed not called. Have we had omitted the cut from the two first prototypes, we would have seen this:

?- #str.default(a,"b",:b)
1>
2>
-> ( "b" ) := 1.00 (0.001) 1
3>

Because each of the prototypes is composed of primitives only, they will be considered sequentially by the solver. In fact, the solver will always considere prototypes sequentially but if a predicate is not a primitive, the following prototype will be considered while the solver waits for answers to the query it put out for the predicate.

As we would expect, if the cutting predicate is not reached by the solver the cut will have no effect as we see in the following example:

?- #str.default("a","b",:b)
1>
2>
3>
-> ( "a" ) := 1.00 (0.001) 1

As you probably noticed in the past examples, we have used as one of the terms :x and :length. These are variables and they can stand for any other type of terms (except variables themselves) during the inference process. See Section 3.6 for more details on variables.

2.4 Prototype

A Prototype defines the relationship between a collection of statements, which may produce a new statement if the logical inference reaches a conclusion. For example, we could create a new logical concept that would contain a prototype based on the weather example we wrote earlier. We will call it surely_raining:

1surely_raining {
2
3    (:x) :- @weather(:x,rain) <0.7|1.0>;
4
5}

A prototype is composed of an entrypoint: a comma-separated list of terms within parentheses followed by a :- and a comma-separated collection of predicates terminated by a semi-colon. The entrypoint specifies what a predicate referencing this knowledge would be like and it is also used during inference to check if the prototype should be used. In this case, it would have a single term that will be unified with the local variable :x. If we wanted to check if it is surely raining in Paris, we would write:

1@surely_raining(paris)

If a caret (^) is insterted between the entrypoint and the :-, it will indicates that during inferences when the prototype’s entrypoint unifies with a statement or a query, no other prototypes should be considered, even if, in the end, the inference fails. This allows for cases where a single prototype among many must be used.

In some instances, it’s often desired to take the negation of a predicate. This can be done by prefixing the predicate with a ! like this:

1!is.string(3.14)

Since 3.14 is a number, the call to the primitive is.string will return a truth value of 0 since that primitive checks if its argument is a string. Negating this will result in the predicate returning 1 as its truth value.

When a prototype contains more than a single predicate, the truth value of the statements matching each predicate will be used to compute the truth value of the predicate as a fuzzy logical and. For example, to answer the question ”Where are we the most likely to see a rainbow?” we would write a new knowledge as follows:

1maybe_rainbow {
2
3    (:x) :- @weather(:x,rain), @weather(:x,sunny);
4
5}

With the weather knowledge we have, we would get the answer honolulu with a truth value of 0.1.

Before moving on to the next concept, lets backtrack to the following example:

1surely_raining {
2
3    (:x) :- @weather(:x,rain) <0.7|1.0>;
4
5}

The prototype could have been written using a constrained wildcard variable:

1surely_raining {
2
3    (:x) :- @weather(:x,rain) _?[lte(1.0),gt(0.7)];
4
5}

Using a variable would have allow us to take in the actual truth value of all the statements satisfying the predicate and use them in whichever way necessary.

2.5 Elemental

Elementals in fizz are the main components of the runtime environment (also called substrate). In most cases, when a knowledge is loaded a new elemental object is created to handle it, however a single elemental can manage multiple knowledges. There are several types of elementals in fizz . See Section 6 for more details. Each elemental presents on the substrate is assigned an unique identifier (GUID), unless one is provided.

Elementals objects can have properties associated with them. In most cases, such data allow for customization or optimization of the objects. This is done with a frame (which is a supported term, see Section 3.3) in between the knowledge’s body and its label, as seen in the following example:

1rand {class = MRKCRandomizer, min = 1550, max = 1650} {
2
3}

In the example we request a specific class of elemental object to be instantiated using the class label and specify a min and max value. While these two properties are specific to MRKCRandomizer, class is a reserved label. There’s a few other reserved labels:

alias a symbol by which the elemental will also be known locally
class a symbol indicating the class of the elemental object
guid a string containing the GUID to be used by the elemental object
spawn assigned to the symbol no will not cause the knowledge to
instantiate a new elemental

If there is no existing matching elemental for a knowledge (that is, no elemental objects with the same name and capable of accepting the knowledge), a new one will be instantiated even if spawn is set to no.

2.6 Service

Services are a special case of elemental objects which exist on the substrate as a singleton. Each of these objects provides services to all other elementals. The services are provided via the classic query/reply pattern shared by all elementals. See Section 7 for more details.

3 Terms

There are eight categories of terms in fizz . In this section we will introduce each one of them and see how they are each different from the other. They all have one thing in common, however: their immutability. While this may be common with atoms, it is less common with more complex data such as lists (at least in non-functional languages).

3.1 Atoms

There are five different kinds of atoms in fizz :

  • Number

  • String

  • Symbol

  • Binary

  • Guid

They are the most basic data that can be handled.

3.1.1 Number

A number in fizz represents a 64-bit numerical value. It can be an integer (signed or unsigned) or a floating point value, depending on how it is written and eventually postfixed. For example, if we consider the following statement:

1yearly_stats {
2
3    (2001,0.4,45u,3f);
4
5}

The first term will be understood as a signed integer, the second term will be floating point, while the third term will be unsigned. The last term, by the addition of the postfix f, will be promoted from signed integer to floating point. Numbers expressed in scientific notation, such as 3e-2 will also be understood as floating point values. For two numbers to be successfully unified, their difference must be smaller than the epsilon value specified in the runtime environment configuration (see Section 4.2).

3.1.2 String

Strings in fizz are no different from other languages: a series of characters between double quotes. For example:

1quotes {
2
3    (DrSeuss,"Don't cry because it's over, smile because it happened.");
4    (OscarWilde,"Be yourself; everyone else is already taken.");
5    (Gandhi,"Be the change that you wish to see in the world.");
6
7}

The common escape sequence using a backslash (for example "\n") is supported with the following characters:

a alert (bell) character
b backspace
f formfeed
n newline
r carriage return
t horizontal tab
v vertical tab

Two strings will only unify if their content and length perfectly match. Note that at this time, Unicode isn’t supported.

3.1.3 Symbol

Symbols in fizz are fundamental. Just like strings, they can contain characters as well as numbers but they are not started and terminated by double quotes. As such, they cannot contain spaces, nor start with a number. They are often used as identifiers. Here are a few example of valid symbols:

1identifiers {
2
3    (jill);
4    (jack74);
5    (bob.phone);
6    (bob.age);
7
8}

Two symbols will only unify if they perfectly match.

3.1.4 Binary

Binary terms are a way for fizz to handle elementals specific binary data. Such terms uses base64 to encode binary contents into a string, and they are specified in fizz code using a single quoted functor as in the following example:

1blobs {
2
3    ('binary("dGhlIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw=="));
4
5}

Two binaries will only unify if there’s a perfect match of the decoded binary data. When a knowledge containing such term is parsed, the parsing will fail if the binary data fails to be decoded.

3.1.5 Guid

Guid terms are a way to represent globally unique identifier. Such terms are specified in fizz code using a single quoted functor as in the following example:

1guids {
2
3    ('guid("71cfade6-3cab-c34e-3ca6-e7a43e6fb5f7"));
4
5}

3.2 List

Lists are common and widely used. They allow the grouping a collection of terms into a single object. The syntax for a list is a comma-separated collection of terms (including lists) in between square brackets. For example, we could have written the color example from earlier where each colors RGB values are expressed as lists:

1color {
2
3    (red,[1.0,0.0,0.0]);
4    (green,[0.0,1.0,0.0]);
5    (blue,[0.0,0.0,1.0]);
6
7}

There’s a special kind of list that can be used to split the content of the list (head and rest). Used with recursion, it makes it possible to iterate over all the terms in a list possible. Consider the following knowledge:

1lst.print {
2
3    ([]);
4    ([:h|:r]) :- console.puts(:h), @lst.print(:r);
5
6}

The above example sets up lst.print with a prototype, which will print the head of the list and then recursively call itself with the rest of the list. The knowledge also constains a statement for when the list is empty. While it is not mandatory, it will cause a call to lst.print to always succeed.

3.3 Frame

In fizz , a frame is the equivalent of a dictionary in other languages. It stores key/value pairs. This is done by having a comma-separated collection of key/value pairs within curly braces. Here is an example:

1gameboy.color {
2
3    ({r = 0.509803, g = 0.784313, b = 0.294117});
4    ({r = 0.325490, g = 0.670588, b = 0.392156});
5    ({r = 0.164705, g = 0.549019, b = 0.349019});
6    ({r = 0.000000, g = 0.294117, b = 0.282352});
7
8}

While the value associated with a key can be any valid term (including a Frame), the key (also called label) can only be a valid symbol. Unlike with lists, unification of two frames will only be done over the labels that both terms have in common.

3.4 Functor

A Functor in fizz is akin to a structure, although it really is more of a named list (since a C-like structure will have fields). Here’s an example where the likelihood of a given weather is given as a functor:

1weather2 {
2
3    (paris,rain(0.5),wind(0.1),sun(0.4),snow(0.1),fog(0.1));
4    (london,rain(0.6),wind(0.1),sun(0.3),snow(0.0),fog(0.7));
5
6}

When it comes to unifying functors. The label of each functor will be unified as well as each of the terms, therefore arity (the number of terms) of each functors also need to be the same.

3.5 Range

Range terms are a way to express a range of numerical values between minimum and maximum values. The syntax of a range is something that we have already encountered in Section 2.3 when expressing the acceptable truth value range for a predicate. Here’s an example where we look at the manufacturer-reported range of some electrical cars:

1car.range {
2
3    (ford(focus),76);
4    (tesla(model_s),<210|315>);
5    (tesla(model_x),<237|289>);
6    (chevy(bolt),238);
7    (nissan(leaf),107);
8
9}

A range will unify with a fellow range but also with a number as long as it is within the range. If we were to query the above knowledge for a car with a range of at least 300 miles, we would do so like this: @car.range(:x,300) and get the variable :x bound to the value tesla(model_s).

3.6 Variable

Variables in fizz , like in any logic programming language, are placeholders for any terms. As we have seen in several examples, the syntax for defining a variable is a symbol prefixed with a colon. Often when unification is happening, it is handy to indicate that we do not care about a given term. For such situations, we use the wildcard variable, which is a single underscore. If we take the car.range knowledge we defined above, we may want to list all the tesla cars, but without caring about the range of each model. We would express this in a predicate as follows: @car.range(tesla(:m),_), and the :m variable will be bound to the values model_s and model_x.

Because inferences in fizz are distributed (within a single substrate or accross multiple networked substrates), the number of replies to a query need to be minimized whenever possible. As such, variables support constraints specifications. Let’s look at an example where we are querying the gameboy.color knowledge we defined earlier:

?- @gameboy.color({r = :r, g = :g, b = :b })
-> ( 0.509803 , 0.784313 , 0.294117 ) := 1.00 (0.001) 1
-> ( 0.325490 , 0.670588 , 0.392156 ) := 1.00 (0.001) 2
-> ( 0.164705 , 0.549019 , 0.349019 ) := 1.00 (0.001) 3
-> ( 0 , 0.294117 , 0.282352 ) := 1.00 (0.001) 4

If we were only interested in the colors where the red component is within 0.1 and 0.4, we could modify our query to use primitives to put constraints on the value bound to the :r variables:

?- @gameboy.color({r = :r, g = :g, b = :b }), gt(:r,0.1), lt(:r,0.4)
-> ( 0.325490 , 0.670588 , 0.392156 ) := 1.00 (0.001) 1
-> ( 0.164705 , 0.549019 , 0.349019 ) := 1.00 (0.001) 2

We now have two matching colors instead of four. However, we did that by filtering the answers we got to our query on the gameboy.color knowledge. By specifying constraints directly on the variable within the predicate, we could have only received the two matching statements:

?- @gameboy.color({r = :r?[gt(0.1),lt(0.4)], g = :g, b = :b})
-> ( 0.325490 , 0.670588 , 0.392156 ) := 1.00 (0.001) 1
-> ( 0.164705 , 0.549019 , 0.349019 ) := 1.00 (0.001) 2

Constraints are specified after a variable with a question mark followed by list or a variable which will be bound at runtime to a list. Each of the element in the list (which can be a functor, range or symbol) is a constraint that any value bound to the variable must satisfy. In the above example, we indicated that the value for :r must be greater than 0.1 and less than 0.4.

Constraints support multiple functors as listed in this table:

gt greater than
gte greater than or equal
lt lesser than
lte lesser than or equal
neq not equal
aeq almost equal
lst.member value is present in a list
lst.except value is not present in a list
lst.incl value is a list that include the items in a list
lst.excl value is a list that exclude the items in a list
is.atom value is an atom term
is.binary value is a binary term
is.string value is a string term
is.symbol value is a symbol term
is.number value is a number term
is.guid value is a guid term
is.list value is a list term
is.range value is a range term
is.frame value is a frame term
is.func value is a functor term
is.unbound no value is bound yet
is.even value is an even number
is.odd value is an odd number
str.find value is a string which contains a specified substring

Most functors requiere a single term except the is.* ones which can be given as a symbol, and aeq which expects two. Constraints can be use on any variables, including in a prototype’s entrypoint as shown here:

1lst.zip {
2
3    ([],[])^                        :- true;
4    ([:e],[:e])^                    :- true;
5    ([:e,:e|:r],:l)                 :- #lst.zip([:e|:r],:l);
6    ([:e,:f?[neq(:e)]|:r],[:e|:l])  :- #lst.zip([:f|:r],:l);
7
8}

3.7 Constant

Constants in fizz are a special kind of variable whose content is static. Aside from the constants defined by the runtime environment, new ones can be defined via command line arguments. Constants do not support constraints, and are prefixed with a dollar sign. The following table lists all the constants provided by the runtime environment:

$true the boolean value for true
$false the boolean value for false
$cores the number of CPU cores enabled for fizz

3.8 Volatile

Volatiles in fizz are a special kind of constant whose content is most likely to change in between unifications. They can be used to add, for example, a time stamp to a statement being asserted (added to a knowledge) like in this example:

?- assert(car(blue,%now))
-> (  ) := 1.00 (0.001) 1
?- @car(:color,:stamp)
-> ( blue , 1503602300.742353 )

The syntax for volatiles is similar to constants, but with a percent instead of the dollar sign. The following table lists all the volatiles currently supported:

%now current time (UTC) in seconds since (Unix) Epoch
%today date and time as a string
%rnd a randomly generated number between 0 and 1
%sym a randomly generated symbol
%gui a randomly generated GUID as a string

Because of their values are always changing, volatiles will always unify with anything. They should really not be used in a statement.

4 Console

4.1 Usage

Because of its asynchronous and concurrent nature, fizz provides a console with a slightly unusual mode of operation. The default state of the console is to display any outputs coming from the runtime or from the queries entered by the user. Here’s the console when the program is started:

$ ./fizz.x64
Fizz 0.1.0-P (20171116.1221) [x64|3]

To switch to input, for example to enter a query or any of the supported console’s command, press the ESC key or one of the arrow keys. When the console is waiting for user input, it will display a ?-. If Ctrl-C is pressed, the console will exit the input state. The up and down arrow keys also serve to cycle thru the history. While, the console is in such mode, any output coming from the runtime will be buffered until the mode is exited. Press the enter key to exit the input mode. If a query or command was entered, it will be executed (in most case asynchronously) and any result will be printed:

Fizz 0.1.0-P (20171116.1221) [x64|3]
load : loading manual.fizz ...
load : loaded  manual.fizz in 0.013s
?- @gameboy.color(:color)
-> ( {r = 0.509803, g = 0.784313, b = 0.294117} ) := 1.00 (0.001) 1
-> ( {r = 0.325490, g = 0.670588, b = 0.392156} ) := 1.00 (0.001) 2
-> ( {r = 0.164705, g = 0.549019, b = 0.349019} ) := 1.00 (0.001) 3
-> ( {r = 0, g = 0.294117, b = 0.282352} ) := 1.00 (0.001) 4

Each solution to a query will be presented as a statement where each variable becomes one of the statement’s terms (in the order they appears in the predicates). The truth value will be printed after, followed by the elapsed time (in seconds) since the query was sent. The last number is a sequential number for the reply. It is worth noting that in fizz a query will not be stopped at the first answer.

When invoking the executable, the arguments of the command line can be any numbers of strings specifying the path and name of files to be loaded by the runtime, as seen in the above example. If the path leads to a folder, it will be assumed that it is a previously frozen runtime enviroment to kindle. The command line option -l can be used to switch the console logging on. This option will expect as argument the path and name of the log file to be created. For example:

$ ./fizz.x64 -l test.log manual.fizz

The command line option -q can be used to specify a query to be executed right after the executable enter its Read–Eval–Print Loop (REPL). Be aware, thought that loading files in fizz is done asynchronously. Therefore a query using any yet-to-be loaded knowledges will fail. For example:

./fizz.x64 -q "/load(\"manual.fizz\")"
Fizz 0.1.0-P (20171116.1221) [x64|3]
?- /load("manual.fizz")
load : loading manual.fizz ...
load : loaded  manual.fizz in 0.013s

Any key pressed while outside of the console input state will cause a console.keypress statement to be broadcasted in the substrate. Any elemental can make use of it (via an activable predicate) and execute inferences based on the key that was pressed. The sole term of that statement is the ASCII code of the key. As an example, here’s a knowledge which display an hint to the user each time it press a key:

1help {
2
3    () :- @console.keypress(_), hush, console.puts("press ESC to enter input mode");
4
5}

Lastly, pressing Ctrl-C outside of the input state, will cause the executable to terminate.

4.2 Adjusting the runtime

Severals parameters of the runtime environment can be adjusted by creating (or modifying) a JSON file. In order for the executable to use that file when it starts, the file must have the same name as the executable and have the exension .json. Here’s an example of a file that adjusts all the possible parameters:

1{
2    "runtime" : {
3        "scheduler" : {
4            "threads"  : 4,
5            "affinity" : true,
6            "spinning" : 500
7        },
8        "offloader" : {
9            "minpool"  : 1,
10            "maxpool"  : 4,
11            "timeout"  : 750,
12            "affinity" : false
13        },
14        "httpclient" : {
15            "dnstimeout" : 3.5,
16            "maxconnect" : 8,
17            "maxrequest" : 4,
18            "maxresolve" : 4,
19            "maxcontent" : 2048
20        },
21        "livereload" : {
22            "enabled"   : true,
23            "interval"  : 250
24        }
25    },
26    "substrate" : {
27        "ttl" : {
28            "type" : "real",
29            "data" : 55.0
30        },
31        "grace" : {
32            "type" : "real",
33            "data" : 0.5
34        },
35        "sspr" : {
36            "type" : "uint",
37            "data" : 8
38        },
39        "pulse" : {
40            "type" : "uint",
41            "data" : 250
42        },
43        "epsilon" : {
44            "type" : "real",
45            "data" : 0.000001
46        },
47        "lettered" : {
48            "type"  : "string",
49            "data"  : "MRKCBFSolver"
50        },
51        "bundle.len" : {
52            "type" : "uint",
53            "data" : 1024
54        },
55        "bundle.tmo" : {
56            "type" : "real",
57            "data" : 0.5
58        }
59    }
60}

It contains two sections: the runtime and the substrate. The former adjusts the threading and multi-cores models of the runtime while the later adjusts the common behavior of all elemental objects will use.

Let’s look at the key/value pairs in the scheduler section:

threads represents the number of threads to be used. This number will not change at any
point in time
affinity if set to true, each thread will be assigned to a given core of the host
spinning the amount of time (in ms) that each thread will spin for when waiting to something
to do before going to sleep. Increasing that value may lower the latency at the cost
of a higher CPU load

The offloader section is responsible for tuning the part of the runtime that handles offloaded processing using a dynamically resizable thread pool. The execution of any primitives flagged as offloaded will be executed on the pool instead of being executed within the elemental object calling it. The key/value pairs meanings is as follows:

minpool the minimum number of threads in the pool at any given time.
maxpool the maximum number of threads in the pool at any given time.
timeout the maximum amount of time a non-busy thread will wait before it exits the pool.
affinity if set to true, each thread will be assigned to a given core of the host.

The httpclient section is responsible for tuning the built-in HTTP client used by the elemental class FZZCHttpPuller. If this section is not present in the configuration file, the HTTP client will not be available. The key/value pairs meanings is as follows:

dnstimeout timeout (in seconds) when performing a DNS lookup.
maxconnect maximum number of concurrent connection to any host.
maxrequest maximum number of concurrent request for the same host (0 for no limit).
maxresolve maximum number of concurrent Domain-name resolution (0 for default).
maxcontent maximum size of the content to store in RAM, before storing it into a
temporary file.

The livereload section deals with the automatic live code reload built in fizz . If this section is not present in the configuration file, this functionality will not be available. The command line option -n can be used to force this functionality to be disabled even if it is enabled in the configuration JSON file. The key/value pairs meanings is as follows:

enabled true to enable functionality, false to disable.
interval interval of time (in ms) in between checks of the loaded scripts file’s timestamp.

Because the substrate section of the JSON file deals with the configuration of each elemental, the format that is expected is a little different. The meaning of each value is:

ttl this is the time to live for anything posted on the substrate (in seconds).
grace this is the grace period for any query (in seconds).
sspr the maximum number of statements to be included in a single query reply. If
there are more statements to be sent, more replies will be sent.
pulse the frequency (in miliseconds) at which each elementals gets to perform cleanups and other
cyclic tasks. The lower the value, the more CPU will be used.
epsilon the upper bound on the relative error due to rounding in floating point arithmetic to be used when
comparing numbers.
lettered default elemental class to be used when creating elemental to handle asserted
statements.
bundle.len the maximum number of statement that can be bundled into a single knowledge before it is asserted
in the substrate.
bundle.tmo the timeout value (seconds) before bundled statements are to be asserted if no other statements
is added to the bundle.

Lastly, there are two command line options of interest: -s and -c. The formost can be used to specify an alternate settings JSON file as show here:

./fizz.x64 -s laptop.json manual.fizz
Fizz 0.1.0-P (20171116.1221) [x64|3]
load : loading manual.fizz ...
load : loaded  manual.fizz in 0.013s

The latter allows constants to be defined as shown in this example:

./fizz.x64 -c user=$USER
Fizz 0.1.0-P (20171116.1221) [x64|3]
?- console.puts($user)
jlv
-> (  ) := 1.00 (0.000) 1

The expected syntax for each defined constants is label=value. The value can be any term while the label is expected to be a symbol. Multiple -c options can be given.

4.3 Commands

Commands differs from queries by starting with a slash. Otherwise, their syntax is similar to a predicate (minus the truth value range). For example:

?- /load("./samples/manual.fizz")
load : loading ./samples/manual.fizz ...
load : loaded  ./samples/manual.fizz in 0.011s

Will load the contents of the manual.fizz file into the runtime.

bye

/bye

Close the console and terminate the executable.

create

/create(symbol,symbol,frame,number?)

Creates one (or more if a fourth terms is provided) elemental object which label will be the first term. The second term is the name of the /em class on which the elemental should be based. The third term contains the properties of the object. For example, to create ten elementals labeled product each with a statements limit of 1000, we would type:

?- /create(product,MRKCLettered,{s.limit = 1000},10)
create : okay.

cpus

/cpus

Print to the console the number of cores the host computer has. This can be handy when you do not know that answer and want to adjust the configuration of the runtime.

?- /cpus
host has 4 CPUs

delete

/delete(symbol|string,symbol|string*)

The delete command allows for elementals to be removed from the substrate. The command will accept any numbers of symbols or string as its terms. The only supported strings are GUID while the symbols can be either an alias or a knowledge’s label. When the later is used, all elementals objects with this label will be removed:

?- /delete(number,fill.it,"3716b075-7d64-2440-eda0-96b1b3e9ae20")
delete : completed in 0.000s

If any of the terms doesn’t resolve into an actual elemental, the command will still complete successfully.

export.csv

/export.csv(string,functor,string,list,frame?)

This command exports statements into a file storing tabular data (numbers and strings) in a plain text format, using the character from the third term as delimiter for the generated lines. The first term indicates the path and filename of the file to be created, while the second term is the predicate to be queried for. The list provided as fourth term contains the index of each columns (starting from 0) to be included in each lines. If provided, the fifth term is a frame which can specify a timeout value (in seconds) after which the command shall complete (with the label tmo); and if the truth value of each statements is to be added as a column (with the label truth). When no timeout is provided, the default is half a second.

As an example, let’s consider the following two knowledges:

1product {
2
3    (model_e,tesla,2012);
4    (iphone_x,apple,2018);
5    (vive,htc,2015);
6    (coconut_water,zico,2000);
7
8}
9
10product {
11
12    (iphone,apple,2007);
13    (iphone_3GS,apple,2009);
14    (7710,nokia,2005) := 0.9;
15
16}

To export all statements with a third term greater than 2005, we would use the command as follow:

?- /export.csv("products.csv",product(_,_,_?[gt(2005)]),",",[0,2],{truth = yes})
export.csv : wrote 5 lines in 0.021s.

Which will generate a products.csv file containing:

1iphone,2007,1.0
2iphone_3GS,2009,1.0
3model_e,2012,1.0
4iphone_x,2018,1.0
5vive,2015,1.

By using an intermediate knowledge instead of directly querying the knowledge that interests us, we could have further filter and/or modify the statements generated. Here’s a simple example which add a GUID to each of the lines that will be stored in the CSV file:

1product.g {
2
3    (:l,:m,:y,%gui) :- #product(:l,:m,:y?[gt(2005)]);
4
5}

The export.csv command will then be:

?- /export.csv("products.csv",product.g(_,_,_,_),",",[])
export.csv : wrote 5 lines in 0.016s.

And it the CSV file contents will be:

1model_e,tesla,2012,3c5b83d9-278e-654a-3c88-07d99d2c1fd0
2iphone_x,apple,2018,5036ef91-7a5f-904b-fa89-771e852f492e
3vive,htc,2015,9369d034-941b-de47-66b8-877da629fae5
4iphone,apple,2007,6fa0953c-f6b4-bd45-8bb7-6e21ab9df9e8
5iphone_3GS,apple,2009,33118137-4253-0241-82ba-951a3ed16de9

export.json

/export.json(string,functor,frame?)

This command exports statements into a JSON file. The first term indicates the path and filename of the file to be created, while the second term is the predicate to be queried for. If provided, the third term is a frame which can specify a timeout value (in seconds) after which the command shall complete (with the label tmo). When no timeout is provided, the default is half a second. Note that only string, number, list and frame can be exported to JSON.

As an example, let’s consider the following gameboy.color knowledge:

1gameboy.color {
2
3    ({r = 0.509803, g = 0.784313, b = 0.294117});
4    ({r = 0.325490, g = 0.670588, b = 0.392156});
5    ({r = 0.164705, g = 0.549019, b = 0.349019});
6    ({r = 0.000000, g = 0.294117, b = 0.282352});
7
8}

If we wanted to export the colors for which the red value if in between 0.1 and 0.4, we would do:

?- /export.json("color.json",gameboy.color({r = \_?[gt(0.1),lt(0.4)]}))
export.json : wrote file color.json

And the generated JSON file will contain:

1{
2    "gameboy.color" : [ {
3        "r" : 0.325490,
4        "g" : 0.670588,
5        "b" : 0.392156
6    } , {
7        "r" : 0.164705,
8        "g" : 0.549019,
9        "b" : 0.349019
10    } ]
11}

Since there was more than one matching statement, the generated JSON object will contain an array with all the frames that were in the statements. The key for that array will be the label of the functor used to query the substrate. If the array only contains a single frame, the frame only will be exported as we can see in the generated file:

1{
2    "r" : 0.325490,
3    "g" : 0.670588,
4    "b" : 0.392156
5}

When the statements to be exported do not contains a single term, all the exportable terms will be exported within a JSON array. For example, if we consider the following knowledge:

1product {
2
3    (model_e,tesla,2012);
4    (iphone_x,apple,2018);
5    (vive,htc,2015);
6    (coconut_water,zico,2000);
7
8}
9
10product {
11
12    (iphone,apple,2007);
13    (iphone_3GS,apple,2009);
14    (7710,nokia,2005) := 0.9;
15
16}

and export it as follow:

?- /export.json("products.json",product(_,_,_?[gt(2005)]))
export.json : wrote file products.json

The JSON file will then contains:

1{
2    "product" : [ [ "iphone" , "apple" , 2007 ]  ,
3                  [ "iphone_3GS" , "apple" , 2009 ]  ,
4                  [ "model_e" , "tesla" , 2012 ]  ,
5                  [ "iphone_x" , "apple" , 2018 ]  ,
6                  [ "vive" , "htc" , 2015 ]
7                ]
8}

freeze

/freeze(string)

This command freezes the runtime enviroment to a binary format that can be kindled at a later point. The only accepted term is the path of the folder in which the saving to be done. Please note that any on-going query is not preserved.

history.cls

/history.cls

Clear the console’s history.

history.len

/history.len(number)

Change the length of the console’s history. The default is 100.

import.csv

/import.csv(string,symbol,string,list,number?,number?)

Imports data from a file storing tabular data (numbers and strings) in a plain text format (using any characters from the third term as delimiter and generates statements from each line. The first term indicates the path and filename of the file to be imported, while the second term is the label to be used for the statements that will be generated. The list provided as fourth term contains the number of each columns (starting from 0) to be extracted from each line of the file and put in the statement. If provided, the fifth term is the number of lines from the file to skip and if there is a fifth term it will be the number of lines to be processed.

If we wanted to import a CSV file such as this:

15.1,3.5,1.4,0.2,Iris-setosa
24.9,3.0,1.4,0.2,Iris-setosa
37.0,3.2,4.7,1.4,Iris-versicolor
46.4,3.2,4.5,1.5,Iris-versicolor
56.3,3.3,6.0,2.5,Iris-virginica
65.8,2.7,5.1,1.9,Iris-virginica

We would do as follows:

?- /spy(append,iris)
spy : observing iris
?- /import.csv("iris.data",iris,",",[])
import.csv : 6 lines read in 0.001s.
spy : S iris(5.100000, 3.500000, 1.400000, 0.200000, "Iris-setosa") := 1.00
spy : S iris(4.900000, 3, 1.400000, 0.200000, "Iris-setosa") := 1.00
spy : S iris(7, 3.200000, 4.700000, 1.400000, "Iris-versicolor") := 1.00
spy : S iris(6.400000, 3.200000, 4.500000, 1.500000, "Iris-versicolor") := 1.00
spy : S iris(6.300000, 3.300000, 6, 2.500000, "Iris-virginica") := 1.00
spy : S iris(5.800000, 2.700000, 5.100000, 1.900000, "Iris-virginica") := 1.0

Since we wanted all the columns to be used, we simply provide an empty list as the fourth term. Also, if a column is detected as holding a numerical value, it will be automatically converted as a number. If we had wanted to convert the last column into a symbol (instead of the string we are getting), we would have had to use an intermediary elemental object which would have made the conversion. Something such as this:

1convert {
2
3    ()  :-  @input(:e1,:e2,:e3,:e4,:l),
4            str.tolower(:l,:l1),str.tosym(:l1,:l2),
5            assert(iris(:e1,:e2,:e3,:e4,:l2),1.0f);
6
7}

It simply states that each time an input statement is broadcasted in the substrate (which is what import does), the last term will be converted to a symbol after having its case changed to lowercase. Finally, a new iris statement is asserted. Running it we now get:

1?- /spy(append,iris)
2spy : observing iris
3?- /import.csv("iris.data",input,",",[])
4import.csv : 6 lines read in 0.001s.
5spy : S iris(5.100000, 3.500000, 1.400000, 0.200000, iris-setosa) := 1.00
6spy : S iris(4.900000, 3, 1.400000, 0.200000, iris-setosa) := 1.00
7spy : S iris(7, 3.200000, 4.700000, 1.400000, iris-versicolor) := 1.00
8spy : S iris(6.400000, 3.200000, 4.500000, 1.500000, iris-versicolor) := 1.00
9spy : S iris(6.300000, 3.300000, 6, 2.500000, iris-virginica) := 1.00
10spy : S iris(5.800000, 2.700000, 5.100000, 1.900000, iris-virginica) := 1.0

import.json

/import.json(string,symbol,list?)

Imports data from a JSON file. The first term indicates the path and filename of the file to be imported, while the second term is the label to be used for the statement that will be generated. If provided, the third term is a list of options to be used for the processing of the JSON objects contained in the file: stringify will keep all strings as string terms, symbolize will force all strings to be converted as symbols. The default behavior is to convert the strings that can be considered symbol as such.

As example, let’s look at importing the foreign exchange rates from such a site as fixer.io22http://api.fixer.io/latest?base=USD. For the sake of simplicity, the JSON file below was abbreviated:

1{
2    "base":"USD",
3    "date":"2017-12-08",
4    "rates":{
5            "AUD":1.3303,
6            "BGN":1.6656,
7            "BRL":3.2733,
8            "CAD":1.2836,
9            "CHF":0.99676,
10            "CNY":6.6197,
11            "CZK":21.764,
12            "DKK":6.3377,
13            "GBP":0.7454
14    }
15}

When we import the file, it will generate a statement containing a single frame. To further process the frame to fit your need, you will need to use some supporting knowledge, so that the right statements can be generated. In the sample etc/samples/fixer.fizz you will find such support code that will process the JSON data from above:

?- /spy(append,conversion)
spy : observing conversion
?- /import.json("./etc/usd-mini.json",input)
import.json : ./etc/usd-mini.json read in 0.001s.
spy : S conversion(USD, AUD, 1.330300) := 1.00 (700.000000)
spy : S conversion(USD, BGN, 1.665600) := 1.00 (700.000000)
spy : S conversion(USD, BRL, 3.273300) := 1.00 (700.000000)
spy : S conversion(USD, CAD, 1.283600) := 1.00 (700.000000)
spy : S conversion(USD, CHF, 0.996760) := 1.00 (700.000000)
spy : S conversion(USD, CNY, 6.619700) := 1.00 (700.000000)
spy : S conversion(USD, CZK, 21.764000) := 1.00 (700.000000)
spy : S conversion(USD, DKK, 6.337700) := 1.00 (700.000000)
spy : S conversion(USD, GBP, 0.745400) := 1.00 (700.000000)

The code in fixer.fizz splits the work over two elementals: process and process.rates:

1process {
2
3    () :-   @input(:f),
4            frm.fetch(:f,base,:base),
5            frm.fetch(:f,rates,:r),
6            #process.rates(:base,:r);
7
8}

The first one, activated when an input statement is published on the substrate, fetchs from the frame it contains the value for the base and rates labels and pass them to the second elemental:

1process.rates {
2
3    (:base,:f) :-   frm.fetch(:f,:l?[is.symbol],:v?[is.number]),
4                    assert(conversion(:base,:l,:v),1.0f);
5
6}

Since the rates are contained in a single frame, the elemental, concurrently fetchs all the label/value pairs from it, checking that they both match the expected type, then a new conversion statement is asserted.

import.txt

/import.txt(string,symbol,number?,number?)

Imports data from a file storing data in plain text and generates a single statements from each line. The first term indicates the path and filename of the file to be imported, while the second term is the label to be used for the statements that will be generated. If provided, the third term is the number of lines from the file to skip and if there is a fourth term it will be the number of lines to be processed. Each of the statement will have two terms: the first being a sequential number (starting at 0) and the second a string containing the whole line:

?- /spy(append,dna)
spy : observing dna
?- /import.txt("./etc/data/U00096.3.txt",dna,1,10)
spy : S dna(0, "AGCTTTTCATTCTGACTGCAACGGGCAATA...AAAAAAGAGTGTCTGATAGCAGCTTCTG") := 1.00 (700.000000)
spy : S dna(1, "AACTGGTTACCTGCCGTGAGTAAATTAAAA...ACTAAATACTTTAACCAATATAGGCATA") := 1.00 (700.000000)
spy : S dna(2, "GCGCACAGACAGATAAAAATTACAGAGTAC...CATTAGCACCACCATTACCACCACCATC") := 1.00 (700.000000)
spy : S dna(3, "ACCATTACCACAGGTAACGGTGCGGGCTGA...GAAAAAAGCCCGCACCTGACAGTGCGGG") := 1.00 (700.000000)
spy : S dna(4, "CTTTTTTTTTCGACCAAAGGTAACGAGGTA...GAAGTTCGGCGGTACATCAGTGGCAAAT") := 1.00 (700.000000)
spy : S dna(5, "GCAGAACGTTTTCTGCGTGTTGCCGATATT...GCAGGGGCAGGTGGCCACCGTCCTCTCT") := 1.00 (700.000000)
spy : S dna(6, "GCCCCCGCCAAAATCACCAACCACCTGGTG...CATTAGCGGCCAGGATGCTTTACCCAAT") := 1.00 (700.000000)
spy : S dna(7, "ATCAGCGATGCCGAACGTATTTTTGCCGAA...CGCCGCCCAGCCGGGGTTCCCGCTGGCG") := 1.00 (700.000000)
spy : S dna(8, "CAATTGAAAACTTTCGTCGATCAGGAATTT...CCTGCATGGCATTAGTTTGTTGGGGCAG") := 1.00 (700.000000)
spy : S dna(9, "TGCCCGGATAGCATCAACGCTGCGCTGATT...GTCGATCGCCATTATGGCCGGCGTATTA") := 1.00 (700.000000)
import.txt : 10 lines read in 0.001s.

kindle

/kindle(string)

This command loads a runtime enviroment from a previously saved binary format. The only accepted term is the path of the folder in which the saving was done. Using kindle and freeze are more efficient than load and save since it use a direct binary format instead of an intermediary text format that would need to be parsed. However, it is not possible to edit the knowledge with a text editor.

knows

/knows(symbol|string|guid)

Check if an elemental object is present on the runtime using its alias (when the argument is a symbol) or its GUID (when the argument is a string or a guid). In the following example, we modify the car.range knowledge to specify an alias for the elemental object that will get created:

1car.range {
2
3    alias = crange
4
5} {
6
7    (ford(focus),76);
8    (tesla(model_s),<210|315>);
9    (tesla(model_x),<237|289>);
10    (chevy(bolt),238);
11    (nissan(leaf),107);
12
13}

We can then use that alias with the /knows command:

?- /knows(c.range)
no
?- /knows(crange)
yes

list

/list

This command generates a list of all the elemental objects presents on the substrate. Each of the output lines, will contains, in order, the GUID, the class, label and, if available, the alias of each elementals:

?- /list
list : 288a77db-bab2-1748-38af-892fcf18d112 MRKCLettered        blobs
list : bf006e31-4bd4-c348-a1a7-0449fb0a167f MRKCLettered        car.range       (crange)
list : 1bb328bb-4938-8a43-9db0-2a1685acc19b MRKCLettered        color
list : 3cfc2da3-8728-0d49-22a0-761d19af28bb MRKCLettered        gameboy.color
list : c9928201-4dbd-5e4d-bab7-ee9e13c771dc MRKCLettered        identifiers
list : 47d3366d-5794-0949-d7a4-f7e462dfaa24 MRKCBFSolver        lst.print
list : 966b0df2-7010-6542-5f83-5cedb64afadb MRKCBFSolver        maybe_rainbow
list : 4048e8be-8adc-0b4a-b880-4968dbaff277 MRKCBFSolver        multiplier
list : 9a5d0527-34d8-ee44-3f9d-7a8522d51cc0 MRKCLettered        product
list : 46d90c88-339d-fa40-da96-3cf068763eca MRKCLettered        product
list : 87240913-e8a1-9e43-3a9c-4b9f47e15b27 MRKCBFSolver        product.g
list : aa7f7a44-d894-c54d-db96-c537d7fb117c MRKCLettered        quotes
list : cfff6ad5-17ce-db43-3d8b-9855d8001539 MRKCRandomizer      rand
list : dd875f1a-9596-a649-7fbb-09420e20396f MRKCBFSolver        surely_raining
list : 6d4e5104-22a2-ee43-3eb3-073f45b08a1e MRKCLettered        weather
list : cb6a0d33-0000-0644-f89a-c7e678060aff MRKCLettered        weather2
list : 45f63bd5-b824-594f-e990-1487247ef64d MRKCLettered        yearly_stats
list : 17 elementals listed in 0.000s

load

/load(string+)

The load command allows knowledge to be loaded from (properly formatted) text files. All terms in the predicate are expected to be strings.

?- /load("./samples/manual.fizz")
load : loading ./samples/manual.fizz ...
load : loaded  ./samples/manual.fizz in 0.011s
?- @gameboy.color(:color)
-> ( {r = 0.509803, g = 0.784313, b = 0.294117} ) := 1.00 (0.001) 1
-> ( {r = 0.325490, g = 0.670588, b = 0.392156} ) := 1.00 (0.001) 2
-> ( {r = 0.164705, g = 0.549019, b = 0.349019} ) := 1.00 (0.001) 3
-> ( {r = 0, g = 0.294117, b = 0.282352} ) := 1.00 (0.001) 4

If any of the files to be loaded have already been loaded, they will each be unloaded before being re-loaded. See the command unload (Section 4.3) to manually unload the knowledge from a given set of files.

reload

/reload(string+)

The reload command allows knowledge to be re-loaded from (properly formatted) text files. All terms in the predicate are expected to be strings.

?- /load("./etc/samples/manual.fizz")
load : loading ./etc/samples/manual.fizz ...
load : loaded  ./etc/samples/manual.fizz in 0.018s
?- /reload("./etc/samples/manual.fizz")
reload : unloading ./etc/samples/manual.fizz ...
reload : unloaded ./etc/samples/manual.fizz in 0.003s
reload : loading ./etc/samples/manual.fizz ...
reload : loaded  ./etc/samples/manual.fizz in 0.018s

poke

/poke(symbol|string|guid,symbol,term)

The poke command allows the properties of an elemental object to be written. For example, in the case of the rand elemental as defined in Section 2.5, we can change the value of its min properties as follows:

?- /poke(rand,min,1545)
?- /peek(rand,min)
peek : min = 1545

In this example, as in the one for the /peek command, we have used the label of the elemental to identify it. If there are more than one elemental responding to the same label, they will all receive and process the poke. In such situation, we should have use the GUID of the elemental to only target a single one.

save

/save(string,symbol*)

The save command allows knowledge to be saved to a (properly formatted) text file, allowing it to be re-loaded at a later time. The command supports saving all knowledges or a selection based on their labels. To save all existing knowledges currently in the runtime environment, you only need to provide the name of the text file to be created:

?- /save("all.fizz")
save: completed in 0.141s.

If we wanted to save only the weather knowledges, we would do:

?- /save("weather.fizz",weather)
save: completed in 0.04s.

All terms except the first one are expected to be symbols.

scan

/scan

The scan command will keep printing statistics on the runtime environment until none of the statistics changes in the substrate:

scan : e:11 k:7 s:2 p:7 u:3.49 t:11 q:3945 r:4384 z:0
scan : e:11 k:7 s:2 p:7 u:3.73 t:1 q:4471 r:5069 z:0 (qps:2191.7 rps:2854.2)
scan : e:11 k:7 s:2 p:7 u:3.98 t:4 q:4995 r:5793 z:0 (qps:2071.1 rps:2861.7)
scan : e:11 k:7 s:2 p:7 u:4.23 t:1 q:5503 r:6498 z:0 (qps:2056.7 rps:2854.3)
scan : e:11 k:7 s:2 p:7 u:4.48 t:2 q:6138 r:7401 z:0 (qps:2529.9 rps:3597.6)
scan : e:11 k:7 s:2 p:7 u:5.00 t:3 q:6843 r:8541 z:0 (qps:0.0 rps:3666.7)
scan : e:11 k:7 s:2 p:7 u:5.25 t:1 q:7789 r:9452 z:0 (qps:3814.5 rps:3673.4)
scan : e:11 k:7 s:2 p:7 u:5.50 t:4 q:8790 r:10426 z:0 (qps:3956.5 rps:3849.8)

The breakdown of the statistic is identical to the stats command with the addition of qps and rps which are respectively queries per seconds and replies per seconds.

spy

/spy(append,symbol+)

/spy(remove,symbol+)

Instructs the runtime to start or stop printing any events (queries, replies, …) related to any of the knowledge labels provided as arguments. Spying is a handy way to see what is happening within the runtime and can be extremly useful to debug. In the following example, we spy on the gameboy.color knowledge then submit a query:

?- /spy(append,gameboy.color)
spy : observing gameboy.color
?- @gameboy.color({r = :r?[gt(0.1),lt(0.4)], g = :g, b = :b})
spy : Q @gameboy.color({r = :r ? [gt(0.100000), lt(0.400000)], g = :g, b = :b}) (14.999830)
spy : R gameboy.color({r = 0.325490, g = 0.670588, b = 0.392156}) := 1.00 (14.999510)
-> ( 0.325490 , 0.670588 , 0.392156 ) := 1.00 (0.001) 1
spy : R gameboy.color({r = 0.164705, g = 0.549019, b = 0.349019}) := 1.00 (14.999510)
-> ( 0.164705 , 0.549019 , 0.349019 ) := 1.00 (0.001) 2

Output from spying will always be prefixed with spy. The first letter after the colon indicates the type of the observed event:

Q a query.
R a reply.
S a statement.
T a query is being scrapped.

stats

/stats

Print to the console some basic statistic about what is happening in the runtime:

?- /stats
stats : e:2 k:1 s:0 p:0 u:1.29 t:1 q:0 r:0 z:

The breakdown of the statistic is the following:

e current number of elemental objects in the substrate.
k total number of knowledges on the substrate.
s total number of statements on the substrate.
p total number of prototypes on the substrate.
u up time (in seconds) of the runtime.
t elapsed time (in miliseconds) it took for the statistics to be collected.
q total number of queries posted on the substrate.
r total number of replies (in statements) posted on the substrate.
z total number of statement posted (without query) on the substrate.

tells

/tells(symbol|string|guid,functor|symbol)

Sends a message (in the form of a functor or a symbol) to an elemental object identified by its label, alias or GUID, the first argument. Not all elemental object can handle message. If the object is identified by its label, all objects with the same label will receive the message.

?- /tells(some.obj,do(this,45))

unload

/unload(string+)

The unload command allows knowledge loaded from a file to be unloaded. All terms in the predicate are expected to be strings.

?- /load("./samples/manual.fizz")
load : loading ./samples/manual.fizz ...
load : loaded  ./samples/manual.fizz in 0.011s
?- @gameboy.color(:color)
-> ( {r = 0.509803, g = 0.784313, b = 0.294117} ) := 1.00 (0.001) 1
-> ( {r = 0.325490, g = 0.670588, b = 0.392156} ) := 1.00 (0.001) 2
-> ( {r = 0.164705, g = 0.549019, b = 0.349019} ) := 1.00 (0.001) 3
-> ( {r = 0, g = 0.294117, b = 0.282352} ) := 1.00 (0.001) 4
?- /unload("./samples/manual.fizz")
unload : unloading ./samples/manual.fizz ...
unload : unloaded ./samples/manual.fizz in 0.000s

wipe

/wipe

The wipe command will cause the runtime enviroment to be cleared of all existing elementals objects. The state of the runtime will be similar to the state at of the runtime when the executable is started.

peek

/peek(symbol|string|guid,symbol)

The peek command allows the properties of an elemental object to be read. For example, if we have a rand elemental as defined in Section 2.5, we can read the value of its min properties as follows:

?- /peek(rand,min)
peek : min = 155

5 Primitives

This Section details the primitives provided by the runtime. For each one, expected (and optional) arguments are described and for most a use case examples is given. All primitives are grouped under related categories.

5.1 Arithmetic

This section contains all the primitives that deal with basic arithmetic.

add

add(number|variable,number|variable,number|variable)

This primitive will unify or bind the sum of its two first terms with the third. For example:

?- add(4,3,:x)
-> ( 7 ) := 1.00 (0.001) 1

If the third term is a number or a variable bound to a number, one of the first terms can be an unbound variable. In that case the primitive will find the right value to make the addition valid as seen in the example below:

?- add(4,:x,7)
-> ( 3 ) := 1.00 (0.000) 1

div

div(number|variable,number|variable,number|variable)

This primitive will unify or bind the division of the first term by the second with the third. For example:

?- div(10,3,:x)
-> ( 3.333333 ) := 1.00 (0.000) 1

If the third term is a number or a variable bound to a number, one of the first terms can be an unbound variable. In that case the primitive will find the right value to make the division valid as seen in the following example:

?- div(:x,3,3.3333333)
-> ( 10.000000 ) := 1.00 (0.000) 1

div.int

div.int(number|variable,number|variable,number|variable)

This primitive will unify or bind the integer division of the first term by the second with the third. For example:

?- div.int(37,6,:x)
-> ( 6 ) := 1.00 (0.001) 1

If the third term is a number or a variable bound to a number, one of the first terms can be an unbound variable. In that case the primitive will find any values that will make the division valid as seen in the following example:

?- div.int(:v,6,5)
-> ( 30 ) := 1.00 (0.001) 1
-> ( 31 ) := 1.00 (0.001) 2
-> ( 32 ) := 1.00 (0.001) 3
-> ( 33 ) := 1.00 (0.001) 4
-> ( 34 ) := 1.00 (0.002) 5
-> ( 35 ) := 1.00 (0.002) 6

inv

inv(number|variable,number|variable)

This primitive will unify or bind the inverse value of the first term with the second. For example:

?- inv(4,:x)
-> ( -4 ) := 1.00 (0.000) 1
?- inv(:x,4)
-> ( -4 ) := 1.00 (0.000) 1

mod

mod(number,number,number|variable)

This primitive will unify or bind results from performing an integer division between the first two terms with the third. For example:

?- mod(9,2,:v)
-> ( 1 ) := 1.00 (0.000) 1
?- mod(8,2,:v)
-> ( 0 ) := 1.00 (0.000) 1

The primitive doesn’t support the first or second term as unbound variables.

mul

mul(number|variable,number|variable,number|variable)

This primitive will unify or bind the multiplication of the first two terms with the third. For example:

?- mul(10,3,:x)
-> ( 30 ) := 1.00 (0.000) 1

If the third term is a number or a variable bound to a number, one of the first terms can be an unbound variable. In that case the primitive will find the right value to make the multiplication valid as seen in the following example:

?- mul(10,:x,4)
-> ( 0.400000 ) := 1.00 (0.000) 1

sim

sim(number,number,number|variable)

This primitive will unify its third term with a value representing the similarity between the first two terms. For example:

?- sim(3.21,3.33,:s)
-> ( 0.785714 ) := 1.00 (0.000) 1
?- sim(3.21,10,:s)
-> ( -0.743261 ) := 1.00 (0.000) 1
?- sim(3.21,-100,:s)
-> ( -0.980808 ) := 1.00 (0.000) 1
?- sim(3.21,2.211,:s)
-> ( 0.000500 ) := 1.00 (0.000) 1

sub

sub(number|variable,number|variable,number|variable)

This primitive will unify or bind the second term subtracted from the first one with the third. For example:

?- sub(10,4,:x)
-> ( 6 ) := 1.00 (0.000) 1

If the third term is a number or a variable bound to a number, one of the first terms can be an unbound variable. In that case the primitive will find the right value to make the subtraction valid as seen in the following example:

?- sub(10,:x,4)
-> ( 6 ) := 1.00 (0.000) 1

sum

sum(number+,number|variable)

This primitive will unify or bind the sum of all terms with the last term. For example:

?- sum(3,3,6,7,:sum)
-> ( 19 ) := 1.00 (0.000) 1
?- sum(3,3,6,7,19)
-> (  ) := 1.00 (0.000) 1

Countrary to the primitive add, this primitive does not support having any term unbound but the last one.

5.2 Basic

Under this grouping are all the primitives that provide very basic - and in most cases essentials - capabilities to the runtime.

assert

assert(functor,number,frame?)
assert(symbol,list,number,frame?)

The assert primitive allows for a statement to be added to an existing knowledge. If no elemental object capable of handling it exists, the runtime will instantiate one. The following example shows how a new statement is added at runtime to the weather knowledge:

?- @weather(seattle,:s)
-> ( sunny ) := 0.20 (0.001) 1
?- assert(weather(seattle,rain),0.6)
-> (  ) := 1.00 (0.001) 1
?- @weather(seattle,:s)
-> ( sunny ) := 0.20 (0.001) 1
-> ( rain ) := 0.60 (0.001) 2

The optional third term to the primitive is a frame which (as we have seen in section 2.2) provides the properties of the statement. Here’s how we could timestamp each statement when asserting them:

?- assert(weather(paris,rain),0.8,{stamp = %now})
-> (  ) := 1.00 (0.000) 1
?- assert(weather(seattle,sunny),0.2,{stamp = %now})
-> (  ) := 1.00 (0.000) 1
?- assert(weather(london,fog),0.9,{stamp = %now})
-> (  ) := 1.00 (0.000) 1
?- assert(weather(mawsynram,rain),1,{stamp = %now})
-> (  ) := 1.00 (0.000) 1
?- assert(weather(honolulu,snow),0,{stamp = %now})
-> (  ) := 1.00 (0.000) 1

When a statement is asserted, it will be broadcasted in the substrate. See primitive repeal for the inverse function.

break

break(boolean)

The primitive break will prematurely end an ongoing inference when its term unify to the boolean value true. The call will always evaluate to a truth value of 1.0.

?- console.puts(a), break(1), console.puts(b)
a
-> (  ) := 1.00 (0.000) 1
?- console.puts(a), break(0), console.puts(b)
a
b
-> (  ) := 1.00 (0.000) 1

See the sample leibniz.fizz for an example of its use.

break.not

break.not(boolean)

The primitive break will prematurely end an ongoing inference when its term unify to the boolean value false. The call will always evaluate to a truth value of 1.0.

?- console.puts(a), break.not(0), console.puts(b)
a
-> (  ) := 1.00 (0.000) 1
?- console.puts(a), break.not(1), console.puts(b)
a
b
-> (  ) := 1.00 (0.000) 1

bundle

bundle(functor,number,frame,number?)
bundle(symbol,list,number,frame,number?)

Like the assert primitive, bundle allows for a statement to be added to an existing knowledge. It however provides a way for the statements provided during consecutive (or concurrent) calls to be grouped into a single knowledge. Once a specified number of statements have been reached, or if the time elapsed since the last addition of a statement reaches a timeout value, the knowledge will be asserted into the substrate. In the following example, we define a procedural knowledge which when triggered (by any line.f statement) will assert a frag statement bundled within knowledges of 1024 statements in size:

1import.frag {
2
3    () :- @line.f(:i,:s), bundle(frag(:i,:s),1,{},1024), hush;
4
5}

If the last term isn’t given, the default value specified in the runtime settings (bundle.len) will be used.

change

change([functor,number?,frame?],[functor,number?,frame?])
change([symbol,list,number?,frame?],[symbol,list,number?,frame?])

The change primitive combines a repeal followed by an assert. In the following example, we use it to replace an earlier version of the statement with one with the current time:

?- change([city.weather.latest(:id,_)],[city.weather.latest(:id,%now)])

Both terms are expected to be lists, describing the statement to be repealed and the statement to be asserted (as per the primitives repeal and assert).

console.exec

console.exec(atom|functor)

This primitive will trigger the background execution of a console’s command. It can be used, for instance by an elemental to trigger the frequent saving of all (or selected) knowledge during the execution. Here’s an example:

?- console.exec(bye)
-> (  ) := 1.00 (0.000) 1
bye!

console.gets

console.gets(variable)

This primitive will read a line from the console. Since the user will be prompted to enter a string as a synchronous operation, calling this primitive will only work when offloaded. For example:

?- &console.gets(:x)
>- hello world!
-> ( "hello world!" ) := 1.00 (5.105) 1

console.puts

console.puts(term+)

This primitive will output the concatenation of the terms in the console. For example:

?- console.puts(Hello,"", world","!")
Hello, world!

declare

declare(list+)
declare(functor,number?,frame?)
declare(symbol,list,number?,frame?)

This primitive will broadcast statements into the runtime environment built from its terms. A functor (or a symbol plus a list) followed by an optional truth value and an optional frame is requiered for the primitive to create a statement. Multiple statements can be broadcasted if they are enclosed in lists. For example:

?- /spy(append,blah)
spy : observing blah
?- declare(blah(23,hello))
spy : S blah(23, hello) := 1.00
-> (  ) := 1.00 (0.001) 1
?- declare([blah(23,hello)],[blah(25,bye)])
spy : S blah(23, hello) := 1.00
spy : S blah(25, bye) := 1.00
-> (  ) := 1.00 (0.002) 1
?- declare([blah(23,hello),0.8],[blah(25,bye),0.5])
spy : S blah(23, hello) := 0.80
spy : S blah(25, bye) := 0.50
-> (  ) := 1.00 (0.002) 1
?- declare([blah(23,hello),0.8],[blah(25,bye),0.5,{stamp = %now}])
spy : S blah(23, hello) := 0.80
spy : S blah(25, bye) := 0.50 {stamp = 1507446180.615446}
-> (  ) := 1.00 (0.002) 1
?- declare([[blah(23,hello),0.8],[blah(25,bye),0.5,{stamp = %now}]])
spy : S blah(23, hello) := 0.80
spy : S blah(25, bye) := 0.50 {stamp = 1507446211.905603}
-> (  ) := 1.00 (0.000) 1

If multiple statements have the same label, they will be grouped according to the runtime environment’s sspr value and broadcasted together.

define

define(symbol,list,list,list)

The define primitive allows for a prototype to be added to the knowledge contained on the substrate. If no elemental object capable of handling it exists, the runtime will instantiate one. The following example defines two prototypes which together print the content of a list given as input:

?- define(lst.print,[[]],[cut],[[[primitive],true()]])
-> (  ) := 1.00 (0.000) 1
?- define(lst.print,[[:h|:t]],[],[[[primitive],console.puts(:h)],[[],[lst.print,[:t]]]])
-> ( :h , :t ) := 1.00 (0.000) 1
?- #lst.print([a,b,c])
a
b
c
-> (  ) := 1.00 (0.002) 1

This would have had the same result as defining the lst.print knowledge as:

1lst.print {
2
3    ([]) ^ :- true;
4    ([:h| :t]) :- console.puts(:h), #lst.print(:t);
5
6}

The first term is the label of the prototype, followed by a list containing the entrypoint. The third term is a list of options ( for example the symbol cut to turns the prototype into a cut one). The last term is a list containing the definitons of all the predicates that makes up the prototype. Each of the predicate is it-self defined within a list. As shown in the above example, this list is expected to have two elements. The first one is a list of options (symbols such as negate, primitive, cut, offload, trigger. The list can also contain a range term and a frame term. The second term can either be a functor or a list containing the label of the predicate and a list of the predicate’s terms.

See the primitive revoke for the inverse effect in Section 5.2.

false

false
false(boolean|variable)

Calling this primitive with no term will cause the on-going inference to fail by resolving to a truth value of 0. When used with a single term it will either test of a value is false or bind a variable to the value false.

forget

forget(symbol+)

The forget primitive will cause all elemental objects with the label given in its terms to be removed from the substrate.

?- forget(product,product.g)
-> (  ) := 1.00 (0.000) 1

fuzz

fuzz(number)

The fuzz primitive will resolve with a truth value during inference the value passed as term:

?- fuzz(0.2)
-> (  ) := 0.20 (0.000) 1

hush

hush

The primitive hush will husher the ongoing inference. No statement will be published and no query will be answered. This is useful mainly in situations where a prototype is activated by a trigger predicate and the goal of it is to

now

now(number|variable)

This primitive will unify and/or substitute its sole term with the current host time (UTC, expressed in seconds since Unix epoch).

peek

peek(symbol,variable|term)

The peek primitive allows for a property of the calling elemental object to be read and unified and/or substitued with the second term. If the label provided as the first term is not a known property, the call will evaluate to a truth value of 0.0. For example, the following knowledge will multiply a value by a factor read from its properties:

1multiplier { factor = 2 } {
2
3    (:v,:v2) :- peek(factor,:f), mul(:v,:f,:v2);
4
5}

Using the console command /poke we can modify the value of the knowledge property on the fly as shown here:

?- #multiplier(3,:v)
-> ( 6 ) := 1.00 (0.002) 1
?- /poke(multiplier,factor,3)
?- #multiplier(3,:v)
-> ( 9 ) := 1.00 (0.002) 1

Accessing properties during inferences can allow for an easier reuse of knowledge. Please note that this primitive will not work when offloaded.

poke

poke(symbol,term)

The poke primitive allows for a property of the calling elemental object to be written with the second term as value. If the label provided as the first term is not a known property or if it is a reserved label (like class guid label alias), the call will evaluate to a truth value of 0.0. Changing the value of a property during inference supports allow for the elemental to save states. The following example uses two properties to cycle through a list of words to only return a different word at each inference:

1wword {
2
3    index = 0,
4    words = [when, why, where, how]
5
6} {
7
8    // the prototype will reset the index to 0 if its value is the size of the words list
9    (:w) :- peek(index,:i),
10            peek(words,:l),
11            lst.length(:l,:s),
12            eq(:i,:s),
13            poke(index,0),
14            false;
15
16    // the main prototype
17    (:w) :- peek(index,:i),
18            peek(words,:l),
19            lst.item(:l,:i,:w),
20            add(:i,1,:i2),
21            poke(index,:i2);
22
23}

Just like with the peek primitive, offloading the execution of the primitive will not work.

repeal

repeal(functor,number)
repeal(symbol,list,number)

The repeal primitive allows for a statement to be removed from any existing knowledge. If the functor or the terms list contains unbound variables, any matching statements will be removed.

?- @weather(seattle,:s)
-> ( sunny ) := 0.20 (0.005) 1
-> ( rain ) := 0.60 (0.008) 2
?- repeal(weather,[seattle,rain],0.6)
-> (  ) := 1.00 (0.000) 1
?- @weather(seattle,:s)
-> ( sunny ) := 0.20 (0.005) 1

Note that the elemental object that was storing the statement will not be detached from the substrate even if it doesn’t hold any more knowledge.

revoke

revoke(symbol,list,list,list)

The revoke primitive allows for a prototype to be removed from the knowledge contained on the substrate. It is the reverse action of the primitive define (see Section 5.2). Using the example from that primitive we can remove both prototypes as follow:

?- revoke(lst.print,[[]],[cut],[[[primitive],true()]])
-> (  ) := 1.00 (0.000) 1
?- revoke(lst.print,[[:h|:t]],[],[[[primitive],console.puts(:h)],[[],[lst.print,[:t]]]])
-> ( :h , :t ) := 1.00 (0.000) 1
?- #lst.print([a,b,c])

Note that the elemental object that was storing the prototype will not be detached from the substrate even if it doesn’t hold any more knowledge.

set

set(term,term)

The set primitive primary use is to assign a value to a variable, but it can also be used to unify terms or variables. When used in the former case, the order in the terms doesn’t matter as shown in the example below:

?- set(:x,4)
-> ( 4 ) := 1.00 (0.000) 1
?- set(4,:x)
-> ( 4 ) := 1.00 (0.000) 1

set.if

set.if(term,term,boolean)

The set.if primitive functions as the primitive set but only if its third term is a number which boolean value is true. If it’s false, it will evaluate to a truth value of 1.0 and the variable will not be bound. For example:

?- set.if(5,:v,1)
-> ( 5 ) := 1.00 (0.000) 1
?- set.if(5,:v,0)
-> ( :v ) := 1.00 (0.000) 1
?- set.if(5,:v,0), set(6,:v)
-> ( 6 ) := 1.00 (0.000) 1

set.if.not

set.if.not(term,term,boolean)

The set.if primitive functions as the primitive set but only if its third term is a number which boolean value is false. If it’s true, it will evaluate to a truth value of 1.0 and the variable will not be bound. For example:

?- set.if.not(5,:v,0)
-> ( 5 ) := 1.00 (0.000) 1
?- set.if.not(5,:v,1)
-> ( :v ) := 1.00 (0.000) 1

then

then(number|variable,number|variable,number|variable,number|variable+)

This primitive will unify and/or substitute its last term with the date/time (UTC, expressed in seconds since Unix epoch) build from the other terms. The first time is expected to be the calendar year, followed by the month and the day. Following optional terms are, in order: hours, minutes, seconds and miliseconds. For example:

?- then(:y,:m,:d,%now)
-> ( 2017 , 12 , 14 ) := 1.00 (0.001) 1
?- then(:y,:m,:d,:h,:min,%now)
-> ( 2017 , 12 , 14 , 20 , 12 ) := 1.00 (0.001) 1
?- then(:y,:m,:d,:h,:min,:s,:ms,%now)
-> ( 2017 , 12 , 14 , 20 , 12 , 21 , 713 ) := 1.00 (0.001) 1
?- then(2018,1,1,:new_year)
-> ( 1514764800 ) := 1.00 (0.001) 1

tme.str

tme.str(number|variable,string|variable)

This primitive will unify and/or substitute its terms in between a date/time (UTC, expressed in seconds since Unix epoch) and a string representation of that date. The first term is expected to be either a number or a variable and the second either a string or a variable. For example:

?- tme.str(%now,:s)
-> ( "Thu, 22 Feb 2018 06:58:23 GMT" ) := 1.00 (0.000) 1
?- tme.str(:t,"Thu, 22 Feb 2018 06:58:23 GMT")
-> ( 1519282703 ) := 1.00 (0.000) 1

true

true
true(boolean|variable)

Calling this primitive will cause the inference to continue. This is sort of a no-op with limited use, except to turn a statement into a prototype. When it is used with a single term it will either test if a value is true or bind a variable to the value true.

whisper

whisper(functor,number,frame?)
whisper(symbol,list,number,frame?)

The whisper primitive allows for a statement to be added to an existing knowledge. If no elemental object capable of handling it exists, the runtime will instantiate one. The following example shows how a new statement is added at runtime to the weather knowledge:

?- @weather(seattle,:s)
-> ( sunny ) := 0.20 (0.001) 1
?- whisper(weather(seattle,rain),0.6)
-> (  ) := 1.00 (0.001) 1
?- @weather(seattle,:s)
-> ( sunny ) := 0.20 (0.001) 1
-> ( rain ) := 0.60 (0.001) 2

Unlike with assert, when a statement is whispered, it will not be broadcasted in the substrate. See primitive repeal for the inverse function.

5.3 Comparaisons

All primitives related to comparing two terms are grouped in this category.

aeq

aeq(number,number,number)

This primitive will evaluate to a truth value of 1.0 if its two first terms are almost equal numbers, and 0.0 if they do not. The third term is the maximum allowed difference between the two numbers to be estimated to be the same. For example:

?- aeq(4.5,4.51,0.01)
-> (  ) := 1.00 (0.001) 1
?- aeq(4.5,4.52,0.01)
-> (  ) := 0.00 (0.000) 1

are.different

are.different(term,term)

This primitive will evaluate to a truth value of 1.0 if its two terms do not unify, and 0.0 if they do.

are.same

are.same(term,term)

This primitive will evaluate to a truth value of 1.0 if its two terms do unify, and 0.0 if they don’t.

cmp

cmp(term,term,variable|term)

This primitive will unify or bind the comparison (lesser, greater or equal) between the first two terms with the third. For example: