French German Italian Spanish Portuguese Japanese Korean Chinese

- Why Types?
- Variable Declarations
- Integers
- Long Integers
- Real Numbers
- Strings
- The Pairing Operation
- The FormulaOne Universe
- Subranges
- Lists
- Summary

So far, the only objects we have worked with have been what we have referred to as simply

Here we should point out the distinction between objects and terms. A term is a FormulaOne expression, a purely linguistic sequence of symbols. An object is an abstract entity that some term refers to. For example, '22 + 3' and '25' are two different and distinct terms, but they both denote the same object, that is the number twenty-five.

There are several reasons. Types make our programs more readable, because we can tell more easily what a variable represents. They also let us pass off to the compiler some of the work of checking arguments in predicate calls. And when the compiler knows what type a variable is, a program can represent it more compactly and access its value more efficiently.

Finally, giving types to variables allows FormulaOne to use decision procedures such as the one it uses for integer arithmetic. Languages which have no types, such as some dialects of Prolog, can't possibly do that because they can't distinguish between integers and any other kind of variable. These decision procedures involve the notion of constraint. Constraints are the pieces of information that FormulaOne places in the environment as it is computing the solution to a query. A constraint can be something as simple as the information that a variable has a certain fixed value, or the information that the sum of a group of variables is equal to another variable. We'll tell you what kinds of constraints can be put on each type of variable as we describe them.

var::type

In this template,

We'll cover some other type expressions that can come after the "::" in due course, and even symbols that can go in place of the "::". At this point we will concentrate mainly on these four basic types.x::Ldeclares x to be a long integerx::Ideclares x to be an integer, having slightly different properties from the long onesx::Rdeclares x to be a real number, that is, a number with a decimal part; andx::Sdeclares x to be a string of characters.

x op n

or of the format

x op var + n

where

Whenever a formula with integers and a relational operator is processed by the query processor, it tries to put it into one of these forms by the laws of algebra. If it cannot, it will try to compute a value for some variable, if that is possible, or raise an exception ('Too many backtracks...') if it is not. So queries like

all s::I & b::I & s = 40 & b = 3*s + 9work with integers, because the value of s is known at the time the last formula is encountered.

All the previous queries will also work with

all s::I & b::I & s>0 & b>0 & 8*s + 6*b = 46should give an exception, because FormulaOne cannot convert the last formula to the proper format for an

The constraints that can be put on

t_{l}v_{l} + t_{2}v_{2} +....t_{n}v_{n} op m

where the

In particular, the spiders-and-beetles formula

8*s + 6*b = 46is in this format already, and will be accepted as a long integer constraint. So as we have seen, the example from the last section will work if we have

For example, consider the query

all x::L & y::L & x > 0 & y > 0 & x * y = 46This query is very similar to the spiders-and-beetles query, but it has one important difference: two variables, whose values are not known, are being multiplied together. Now we know that the pairs of possible values for

all x::L & y::L & x > 0 & y > 0 & y < 50 & x * y = 46Then, FormulaOne could solve the equation by testing all values of y between 0 and 50 (not inclusive).

Your own knowledge and programming skills can be brought to bear on a problem. In this example, considerations of basic mathematics would lead you to an optimized query:

all x::L & y::L & x > 0 & y > 0 & y < 24 & x * y = 46FormulaOne will only test for y from 1 to 23 (obviously 24 or greater will not divide evenly into 46).

It might seem that you could optimize the query further by adding in

Real-number constants are of the form

[-]<digits>.<digits>where <digits> is a sequence of one or more digits. (Note that there must be at least one digit before and after the decimal point.) For example, 232.45, -9999.0, 0.8937432, and 3.14 are all real constants, and can be compared to variables of type

Real-number constants can also be followed by an optional expression of the form

e<digits>or

e-<digits>indicating an exponent of 10 by which the constant will be multiplied. For instance, 2.17el0 represents the real number 21,700,000,000, and 314.15e-02 represents 3.1415.

A full decision procedure for real-number variables exists, but is very complex. The only constraints you can put on real variables in FormulaOne are of the format

x op n

or of the format

x op var + n

the same as for integers. Note, again, that this doesn't mean that you can't do arithmetic operations with real variables. You can do any addition, subtraction, multiplication, and division you want with them, as long as their values are known at the time the expression gets evaluated.

Constant terms of type

'Maple Leaf' ' $22.34 US' ' 'That last one is the

'The word ''word''.'contains only the characters in the following line:

The word 'word'.You might now recognize that the

Print('wacka wacka') Print('3.1415', 'nine') Print('A B C ')The term

For instance, the query

Len('Prolog', 6)succeeds because the string 'Prolog' contains six characters; but the query

Len('four', 7)fails. Of course, Len can be used to find the length of a given string. The query

all Len('$22.34 Cdn', length)should give the single solution 'length = 10 '. The first argument to

Append('foo','bar',str)will result in str = 'foobar', while

Append('foo ','bar', str)will result in str = 'foo bar'.

Single characters can be extracted from strings by direct indexing. The first character has the index 0. The query

all s = 'Vancouver' & s(3) = ch & ch = "c"should succeed and give the solution ch = 99 for the variable ch. Characters are in FormulaOne represented by their Ascii codes taken as integers. The Ascii code for the character c is 99. The same integer is denoted by the character constant "c".

Here is a predicate which will allow us to take a string and pad it with periods (".") to 10 characters, if it is not that long.

pred Dotpadded(string::S, outstring::S) iff {'string', padded to at least 10 characters, is 'outstring'} Len(string, x) & (x >= 10 & outstring = string | x < 10 & Append(string, '.', newstring) & Dotpadded(newstring, outstring))With these predicates declared, the following queries should all succeed:

all Dotpadded('Chapter 9', 'Chapter 9.')

all Dotpadded('Andrews', 'Andrews...')

all Dotpadded('Srivallipurandan', 'Srivallipurandan')

(22,3) ('Dixie',0) (x,(929.29,last))As you can see, any two terms, be they integer, real or string, constant or variables, can be paired. The last example above even contains a pair as one element of a bigger pair. The comma operator, like an arithmetic operator, constructs a new term from two other terms on either side of it. The difference is that (22,3) is not equal to any other simpler term, whereas (22+3) is just another expression for 25. The type that every pair term belongs to is the type

(x, 929.29, last)is equivalent to

(x, (929.29, last))and not to

((x, 929.29), last)

Every term we will encounter will refer to some object in this universe. Types are just subsets of this universe. The type consisting of the entire universe is called the

This means that when a term of one type is given as an argument to a parameter of another type (the situation usually referred to as a type clash in other languages), it doesn't necessarily cause a problem. Say we had the following predicate defined and compiled:

pred Determ(a::R, b::R, c::R, det::R) iff det = ( (b*b) - (4*a*c) )Then we could run the following query with no problems:

all det x::R & x=2 & Determ(x, 3, 4, det)even though the terms 2, 3, and 4 are integer constants, not real constants. Computationally, of course, FormulaOne has to convert the value of x from a 32-bit integer into a 64-bit real number, but it does so automatically because the value is logically a valid member of the type

The basic types of the universe are all referred to by name identifiers. But you can also use more complex type expressions to refer to many different useful types. The forms of type expressions have been chosen to represent the kinds of types that programmers frequently want.

all s::[1..10] & b::[1..10] & 8*s + 6*b = 46We could read this, in English, as "s and b are between 1 and 10, and 8*s + 6*y is equal to 46." It is equivalent to saying

all s::I & s>=0 & s<=10 & b::I & b>=0 & b<=10 & 8*s + 6*b = 46Expressions of the form

[n..m]

for any numbers n and m, stand for the type containing all the integers from n to m. They act just like the type names

ftemp::[-32..212] delta:[-1..1] son:[1..7]These type expressions are called subranges, and will become particularly important when we talk about arrays. But even here, they have the effect of making the program code more compact and readable, which is always a benefit. Normally the variables declared as being of subrange types are represented as

L[n..m];

for example

ftemp::L[-32..212]

(1, 1, 2, 3, 5, 8, Nil) Nil (32767, Nil)The set of all objects of type list I can be defined recursively as follows:

**Nil**is of type**list I**;- The pair
*(h, t)*is of type**list I**if*h*is of type**I**and*t*is of type**list I**; - Nothing else is of type
**list I**.

We can use lists of other types too, for instance

{Line 1} pred Sum(l::list L, sum::L) iff {Line 2} {The sum of the list 'l' is 'sum'} {Line 3} l = Nil & {Line 4} sum = 0 {Line 5} | l = (head, tail) & {Line 6} Sum(tail, tailsum) & {Line 7} sum = head + tailsum

The formula

head::L tail::list LFor example, if

Sum(tail,tailsum)

The predicate

In computing the query

We go back to line 1 and replace

The next recursive call in line 6 in

Control passes "back up one level" and line 7 is executed:

Next control passes up to the first recursive call where head was 3 and line 7 executes and gives a final

There are other and better ways to achieve what

Lists are recursive data structures, so recursive predicates fit them well. You will frequently encounter recursive predicates, and the need for them, when you work with lists. There are several predefined predicates for lists. Len and Append, which we encountered for strings, also work with any kind of list type as well as strings. (They are, technically, polymorphic predicates - they can take many types of terms as arguments.) Len gives the number of elements in the list, and Append makes a list out of the elements of the first list, followed by the elements of the second.

There is also a list membership operator, in, which you can use to test or assert that a term is in a given list. The following query should succeed:

all x::I & z::list I & x = 2 & z = (3, 2, Nil) & x in zthe test

all x::I & z::list I & z = (3, 2, Nil) & x in zshould give you two solutions, one with x = 3 and the other with x = 2.

- The four basic types of FormulaOne are
**I**(integers),**L**(long integers),**R**(real numbers), and**S**(strings). - Types are subsets of the FormulaOne universe.
**I**is a subset of**L**, which is a subset of**R**. **L**integers can have more general and powerful constraints put on them than**I**integers.**Subrange**type expressions allow you to assert that an integer variable is in a given range.- Variables of a
**list**type expression can have values consisting of lists of values of a given type, terminated by**Nil**. - There are several predefined predicates in the FormulaOne Runtime Library (F1RTL), which make working with strings and lists easier.

Top Predicates Modes