Symbolic Logic:Programming:Object Oriented Logic Programming

Object Oriented Programming may be regarded as a combination of,
 * Modularity, Abstraction and Encapsulation
 * Inheritance
 * Construction and Messaging

Inheritance is based on set theory, with an additional logical exception system (function overriding).

Modularity, Abstraction and Encapsulation
The basis for OO is that functions and data belong together to describe a single object that represents the behaviour of a real world entity.

To support this syntactically functions are defined in a single class definition. Data variable and functions of the class are accessed as members. In member functions the class is an implicit first parameter to the function.

is used instead of,

Logically both these calls should mean exactly the same thing, but in most OO languages these are calls to different signatures.

The signature of a function is the combination of name and other information that uniquely identifies a function, in a language. The definition of a signature differs from language to language.

Abstraction and Encapsulation is the hiding of member functions and member variables so that they may only be accessed from certain scopes. Member functions and variables may be
 * public - accessible outside the class
 * private - accessible only within the class

Class inheritance (described in the next section) introduces two other forms of scope restrictions.
 * protected - accessible from this class or an inheriting class (this is the meaning of protected in C++).
 * external - accessible from outside the project (this is the meaning of protected in java).

Both these are useful and significant meanings and it is unfortunate that, in the java language, the same word is re-used for a different meaning.

Classes and Set Theory
Inheritance is related to set theory, but with an exception system (for overriding functions).

Inheritance - "Is A" relationship
Class inheritance in object oriented programming may be thought of as an implementation of set theory where,
 * A class is identified as a set.
 * The "is a" inheritance relationship is identified with the subset relationship.
 * A method or function is an implication rule.

For example where there is a class Dog and a class Animal, in set theory there are corresponding sets Dogs and Animals. Dog inherits Animal then means that the set Dogs is a subset of the set Animals.

For example take the rule, "all dogs chase cats". In set theory this may be written,


 * $$d \in Dogs \and c \in Cats \and d.See(c) \implies d.Chase(c) \!$$

Which may be written as a class method,

However there is a problem. What happens when there is a class CatLovingDog that inherits from Dog. A "cat loving dog" would greet a cat, not chase it. To allow for this the logic would be changed to,


 * $$CatLovingDogs \subset Dogs \!$$
 * $$d \in Dogs \and c \in Cats \and d.See(c) \and \neg d.ChaseException \implies d.Chase(c) \!$$
 * $$d.ChaseException = (d \in CatLovingDog) \!$$
 * $$d \in CatLovingDog \and c \in Cats \and d.See(c) \implies d.Greet(c) \!$$

In Object Oriented programming this would be written as,

In effect overriding virtual functions in an object oriented language creates a exception condition system represented here by the d.ChaseException condition. There is no way to turn this exception system off.

An exception system is where I make a statement, but qualify it with an exception. In our example, we make the statement,
 * "all dogs chase cats"

but I qualify it with the exception,
 * "except cat loving dogs"

Statements that are qualified by exceptions cannot be relied on, and should not be used as a logical basis for functionality. Instead exceptions should be used sparingly, in non key areas. Statements qualified by exceptions are default statements that are usually true, not always true.

Suppose I have a class CatHatingDog. Clearly "cat hating dogs" chase cats. So the rule is,


 * $$CatHatingDog \subset Dog \!$$
 * $$d \in CatHatingDog \and c \in Cat \and d.See(c) \implies d.Attack(c) \!$$

by combining this with the rule for Dogs,


 * $$d \in Dogs \and c \in Cats \and d.See(c) \and \neg d.ChaseException \implies d.Chase(c) \!$$

we get,
 * $$d \in Dogs \and c \in Cats \and d.See(c) \and \neg d.ChaseException \implies d.Chase(c) \and d.Attack(c) \!$$

CatHatingDogs both chase and attack cats. But to to get back what we already new about Dogs, we need to add the line super.See, which may be expanded as Chase(cat).

Note that mutliple statements separated by ";" mean the (and) conjunction of the two statements.

The Inheritance Theorem
A statement made about a set is true for all subsets. This may be expressed in logic as,

$$\forall f \in function, p, c \in set : (c \subset p \implies ((\forall x \in p : f(x)) \implies (\forall y \in c : f(y)))) \! $$

This theorem is easily proved using, $$(X \subset Y and x \in X) \implies x \in Y \!$$

For classes, this theorem means that a function that is defined for a class is also defined on any inherited class.

This statement involves quantification over functions which is not allowed in first order logic. So this is second order logic.

The inheritance theorem gives the parent class a way of calling functions a derived class. This means that the parent class and the inherited class may have a two way interaction.

Suppose a class Club represents a club that you may join if you meet certain conditions. The membersip condition might be called ElligibleForMembership.

Then there might be a club for dog owners called DogOwnersClub which inherits from Club.

The Club class may call the DogOwnersClub::ElligibleForMembership function without ever knowing that there is a DogOwnersClub class. Also the DogOwnersClub class has access to all the members of Club. This two way relationship is very useful in writing general re-usable code.

Inheritance - "Has A" relationship
A "Has A" relationship differs from an "Is A" relationship in that there is no set membership implied. A dog has a leg does not mean that a dog is a leg. What it means is that part of a dog is a leg. We might say that a dog has a dog leg, and that a dog leg is a leg. In fact a dog has four legs, Dog.HasA(Leg, LeftFront) Dog.HasA(Leg, RightFront) Dog.HasA(Leg, LeftRear) Dog.HasA(Leg, RightRear)
 * Left Front
 * Right Front
 * Left Rear
 * Right Rear

How does the leg communicate with the dog? With an inheritance relationship the parent class has access to call methods on the dervived classes. The same functionality is useful in a "Has A" relationship.

Suppose the leg has a method FeelsPain. We would like calling this method on the leg to invoke a response on the Dog. But because there are 4 legs there would need to be 4 methods implemented on the dog,

These names are constructed using the name pattern FeelsPain% where % is replaced by the name of the leg. The functions Name and Rename map the name pattern to the name. Constructs the function name by removing the %. Constructs the function name by replacing the % by the name.
 * Name(FeelsPain%) = FeelsPain
 * Rename(FeelsPain%, LeftFront) = FeelsPainLeftFront

Using these functions a rule similar to the Theorem of Inheritance which may be applied to HasA relationships,

$$\forall f \in pattern, p, c \in set, n \in name, x \in p  : \!$$
 * $$(Name(f)(x) \implies (\forall y \in c : x.HasA(y, n) \implies Rename(f, n)(y))) \! $$

This extension of the Theorem of Inheritance is the axiom of renaming inheritance.

Using Renaming Inheritance
This would result in Feels pain in left front leg

being written to the log.

Classes and Sets
Inheritance may be seen as logic. The approximate correspondance between classes and sets is shown in the table below. The correspondance is approximate because C++ is an imperative language (and not very clean).

There are simple laws that describe the set theory related to inheritance.

So inheritance can be defined in terms of logic.

But what about member variables?

Member Variables
Member variables are an implementation strategy for recording facts about objects. A member variable allocates memory to store the value with the object. A fact,

x.GetName = "Bob"

This kind of fact is called an attribute. It is implemented using a member variable,

x.name = "Bob"

Without member variables the fact would need to be recorded in a (hash) table of facts.

If class A is a subset of class B, all attributes of B are attributes of A. So memory needs to be allocated for member variables to record the attributes.

Conclusion
All of inheritance may be implemented using simple logic. Member variables are an implementation strategy for efficiently recording facts in memory.

In Meta Programming we will see how this logic may be unravelled at compile time to give an efficient implementation.

The language structures of Object Oriented programming are useful to make programming more understandable to the programmer.

Criticisms of Object Oriented Programming

Event handlers / Callbacks
Class inheritance gives the ability for the inherited class to call a function on the inheriting class, without knowing the inheriting class. For example (in C++),

The Vault class is able to call the Alarm method without the implementation of the Alarm method, or the class to which the Alarm method belongs.


 * $$Banks \subset Vaults \!$$
 * $$v \in Vaults \and v.Open \and \neg v.Authorised \implies v.Alarm) \!$$
 * $$b \in Banks \and b.Alarm \implies b.logger.Write(message) \!$$

Callbacks
The previous example used inheritance as a convenience. A bank is not a vault. A bank has a vault, and may have a number of vaults. Maybe the bank has a main vault and a secondary vault.

This can be represented in logic as,


 * $$Bank.HasA(Vault, main) \!$$
 * $$Bank.HasA(Vault, secondary) \!$$
 * $$b \in Banks \and b.AlarmMain \implies b.logger.Write(...) \!$$
 * $$b \in Banks \and b.AlarmSecondary \implies b.logger.Write(...) \!$$

When the main vault alarm goes we would like the AlarmMain method to be called on the bank. Similarly for the secondary vault alarm.

Now we need some linking logic to link up the vault alarm method to the relevant bank alarm method.
 * $$Bank.HasA(Vault, Main) \and b \in Banks \implies b.Main.Alarm \iff b.AlarmMain \!$$
 * $$Bank.HasA(Vault, Secondary) \and b \in Banks \implies b.Secondary.Alarm \iff b.AlarmSecondary \!$$

These rules may be generalised as,
 * $$Bank.HasA(Vault, x) \and b \in Banks \implies b.x.Alarm \iff b.x.CallbackAlarm \!$$

where,
 * $$b.Main.CallbackAlarm = AlarmMain \!$$
 * $$b.Secondary.CallbackAlarm = AlarmSecondary \!$$

It is not simple to code this in C++. C++ distinguishes between functions and member functions. To implement the above code would require member function pointers. Java does not support function pointers so the result must be achieved using an interface. C# has delegates for the implementation of callbacks.

From a logic point of view, a member function is a function. So,


 * this->Alarm is equivalent to Alarm(this).

In any case the use of function pointers makes the code hard to understand. Also the callback functions must be initialized in the Bank class. The Bank class must do its own plumbing. When we used inheritance there was no need for call back functions at all. Is there a way of treating the "Has A" relationship so that is similar to "Is A"?

Renaming Inheritance
The Bank needs to map the methods AlarmMain and AlarmSecondary onto the Alarm method. We could think of this as,


 * AlarmMain <-> Alarm% with % replaced by Main
 * AlarmSecondary <-> Alarm% with % replaced by Secondary

Alarm% is a name pattern. By defining it in the Vault class we already know the names of the functions to be called.


 * class Vault
 * protected:
 * abstract void Alarm rename as Alarm%; // Alarm% is the template for the name in the inheriting class.
 * private:
 * void Open
 * if (not Authorised)
 * Alarm;
 * }
 * }
 * }
 * Alarm;
 * }
 * }
 * }

The Bank class has two vaults called Main and Secondary. It needs to respond to the Alarm raised from either Vault.


 * class Bank
 * inherit Vault as Main
 * inherit Vault as Secondary
 * // In heriting from Vault as Main Bank, inherits the Alarm method which is renamed.
 * // The rename template Alarm% is used with % substituted with Main to give the inherited name.
 * void AlarmMain
 * Logger.Write("Alarm on main vault");
 * Alarm;
 * }
 * // The rename template Alarm% is used with % substituted with VaultSecondary to give the inherited name.
 * void AlarmSecondary
 * Logger.Write("Alarm on secondary vault");
 * Alarm;
 * }
 * }
 * Logger.Write("Alarm on secondary vault");
 * Alarm;
 * }
 * }
 * }

The use of a name pattern gives the Vault class more control of how it is to be used. It is not dependent on function pointers being set up. The inheriting class needs only to implement the renamed functions to implement the call back.

Typical Usage
Renaming inheritance is readily applicable in many simple situations.

Get Set methods on attributes
Often developers will write Get and Set methods to allow controlled access to a member variable. These methods may be inherited from a class called Attribute, using renaming inheritance,

The person class will then inherit the methods,
 * GetFirstName
 * SetFirstName
 * GetAge
 * SetAge

The main benefit of this is to put functionality into the Attribute class that would otherwise be implemented for each attribute. See Object Relational Mapping - Attributes.

Owned Lists
Where a class owns a list of objects, it will need to keep control of the list. For example the Person class may have a list of Pets. The Person class will want to control the addition of new animals to the list of pets.

The Person class inherits the functions,
 * InsertPet
 * GetPet
 * ValidatePet

The list may be given to another class for some processing. If the other class calls the Insert method the ValidatePet method will be called. So the person keeps control of the list.

Shared Functions
If the % is ommitted from the renaming template then if the base class inherits the same class multiple times the same signature may be implemented multiple times. This is called signature sharing. In fact signature sharing may occur without using renaming inheritance.

Logically in this case all the implementations should be inherited. A call to a function should invoke all functions that match the signature.

The logical interpretation is that all these separate implementations should be consistent. They should return the same result given the same inputs.

But it is helpfull to provide another interpretation of this. A combination operator may be provided that combines the results together. For a function that returns a boolean this operator would be "and" by default.

Shared inheritance allows code the traversal of an object structure. For example it allows ever attribute of an object to be visited for purposes such as validation or saving data to a database.

Closures
A Closure is a way of constructing a function that is useful for implementing a call back function. It provides an alternative to renaming inheritance for when the callback function is not defined by the structure of the classes.

It is a combination of Lambda Calculus and nested functions. Nested functions give access to the local variables from the enclosing function.

Lambda Calculus is a simple but powerfull idea that the formal parameter belongs in the code instead of the function name. This allows a degree of flexibility with the use of parameters that has staggering power and expressiveness. In fact the Lambda operator alone gives a language which is sufficient to write any program.

However the art of programming is essentially about writing programs that other people may have a chance of understanding. The role that Closure have for this purpose is to allow programmers to construct there own language control structures (for loops, if statements, ...). The classical example of this is the ForEach method on a list,


 * listBooks.ForEach(book : book.Read);

Here "book : book.Read" acts like a function taking a parameter (the book), and calling the Read method on it. The utility of this is its accessibility and readability. If we had a static function Read that took a book as a parameter we could have written,


 * listBooks.ForEach(&Read);

The code is shorter but harder to understand. We have to look at the definition of Read to find out that it is a function that takes a parameter before we can guess at the intent of the code. This is why function pointers are avoided by most programmers.

The term Closure appears to refer to the access to local variables. However it is good practice for a loop structure to call only one function in its body. This allows the action performed repeatedly to be documented and described individually. Smaller functions with a single simple purpose are easier to understand.

Closures may be implemented by Lambda lifting.

Mappings
A mapping is a data type (i.e class) that associates one set of objects with another set.

A mapping may be declared,

where X is the image and Y, Z ... is the domain.

for example

The range of values may be restricted using a range type.

A mapping is a class and may be inherited. For example,

all mappings implement the functions,

Also ever member function in the image class is implemented in the mapping. The implementation depends on the renaming.

For example,

then Butterfly[1..10] has the following methods implemented automatically,

Functions whose name is shared may be used to traverse the objects in a mapping.

The Lookup and the ForEach functions are overidable.

If the domain classes implement a function called Hash returning a long it will be used in implementing a hash table.

There is no way of telling if an object has been added to the mapping yet, because this would not make sense in logic. The functions Size and ForEach both finalise the mapping so that no more objects may be added.

Class Sections
Class sections divide the functionality in a class. For example each object needs functionality for,


 * Persistence and Selection
 * Graphical User Interface
 * Messaging.

The sections in a class may relate to different libraries.

An instance of a class may not need every section for a particular purpose. For example if no GUI is required for a particular class instance the GUI section will not be used.

Construction and Messaging
Object Oriented programming is built on the principle that the functionality should be attached to the data. But when sending messages this is not the case.

When sending messages the whole message should be constructed in a structured form, with no functionality.

Dynamic Class Inheritance Statements
Class inheritance declarations like,
 * inherit A
 * inherit A as B

are unconditional and fixed at compile time. Class inheritance statements support also describe inheritance but they may be conditional.

These inheritance statements may be used freely in the scope of a class definition and within function definitions. These statements may be within if statements that make the inheritance conditional. The execution of these functions to evaluate the types of variables usually occurs in Inheritance resolution.

However it is possible for the inheritance to be undecided until the Execution Meta Phase. Where there is a dynamic inheritance statement which cannot be resolved as true or false in the Inheritance Resolution phase there must be support for inheritance which is activated in the execution meta phase. The truth of the inheritance statements becomes a pre-condition on inherited methods.

The fixed syntax for inheritance declarations is encouraged for readability over inheritance statements as they are more understandable.

Two Stage Call
Each call to a function is implemented as 2 stages. For each function implementation matching the name, and number of parameters (arity) there is a,
 * Precondition - Identify the types, along with any other pre-conditions.
 * Body - If the pre-condition passes then execute the body of the call.

Every call to a function first calls a router function that matches the name and arity. For each implementation function matching the name and arity the router function does

For example,

Then the router function for MoveTo would be,

A call to a function,

is equivalent to,

when expanded out by Partial Evaluation the code becomes equivalent to,

The generated code needs to be equivalent to the implementation described. Because of Partial Evaluation in a particular call context often the type will be known and the router function expanded. However if not the router function may make use of a VTable in the generated code.

Pre-conditions
Pre-conditions are implicitly defined by parameter types. But they may also be added explicitly. For example

is equivalent to,

Characteristics and Pre-conditions
The characteristics input("in"), output ("out"), and unique may be used to create conditions that control the order of calculation.


 * in - The parameter value will already have been calculated before the function is called.
 * out - The parameter value will not have been calculated.

The pre-conditions unique and multiple determine if Value Sets are needed for parameters,
 * unique - There is only one value (no need for a Value Set).
 * multiple - There is more than one value (need a Value Set).

For example,

is a function that calculates its return value from its input value. The characteristic conditions may also be written,

Testing to see if a value is already calculated (Known) is not generally allowed in CLP. For this reason input and output characteristics are not part of the precondition. The characteristics are tested separately in the characteristic condition, to determine which of the logically equivalent implementations are ready to be run now.

When there are multiple implementations with the same name, arity and precondition, the characteristic conditions determine which function implementation is used, and when. It is up to the developer to insure all implementations with the same pre-conditions but different characteristics are logically equivalent. The characteristics only choose which implementation is used.

Characteristics may delay the execution of the implementation of a function. If none of the characteristic conditions are met the call is placed on a queue to be run later when the characteristic conditions are met.

Characteristics should be used sparingly. If providing multiple function implementation with the same pre-condition, but different characteristics, the developer must insure that each implementation is logically consistent.

Characteristics should only be used,


 * At interface methods with data coming from the outside world.
 * To define primitive functions.

As a general principle it is better to leave out information that Meta Phase 1 will calculate.
 * Types that may be deduced from context.
 * Characteristics.
 * Parameters that may be deduced from roles.

This results in code that easier to re-use and modify.

Note: Characteristics are similar to modes in [Mercury].

Implementation of Inheritance
The architecture chosen to implement multiple inheritance differs considerably from the C++ implementation. The C++ implementation is not suitable for fine grained inheritance. By fine grained inheritance I mean a class composed of multiple small single purpose sub classes.

The C++ architecture uses one VTable pointer for each inherited class that has virtual functions. Also there are extra pointers associated with the use of virtual inheritance. For logic, all inheritance is virtual, and all functions are virtual.

The architecture chosen is non-hierarchical. Each inherited class is represented by a structure within the instance, called the implementation, which contains the local variables. The instance has a single pointer to a static structure that stores the identity information of the class.

The identity object implements the QueryInterface which returns interfaces to the object. The interface is a structure, but is similar in effect to a VTable.

A pointer is represented by an ObjectPointer, which includes two pointers,
 * A pointer to the underlying object.
 * A pointer to the interface.

The purpose of the ObjectPointer is to remove the need for each inherited classes implementation to store its own VTable pointer. This makes the class instance smaller at the cost of longer pointers.

When a class is inherited without renaming, an ObjectPointer may be created from a pointer to the interface. When a class is inherited with renaming the interface pointer records which inherited class implementation is being pointed to.

Internal and External Names
When there is renaming inheritance there may be internal and external names for each function.


 * The internal name is the name of the function in the inherited class.
 * The external name is the name of the function after renaming as it appears in the instance class.

Otherwise the internal and the external names will be the same.

The internal name and the external name will be diferent if there is renaming, and the function external name depends on the name for the inherited class.

The external name is represented by the "rename as" clause, with the % replaced by the "as" name.
 * The inheritance
 * The renamed function

Object classes
The Object classes are used in implementing the inheritance architecture.

Object Identity
The base class for identity.

Object
Base class for all object instances. No virtual functions here.

Object Pointer
The ObjectPointer acts like a pointer but stores the interface.

Calling Functions
The -> operator of the Operator Pointer returns a table of pointers to static functions. The Operator Pointer needs to be passed to the static function as the first parameter.

Classes for a Logical Class
To implement the required architecture, multiple physical C++ classes are needed to implement a single logical class. The classes are,

Implementation
The implementation class has all the member variables and functions implemented in the class. But is not instantiated directly. It represents the class, minus the inheritance, with the instance class creating the inheritance structure.

All the functions are static with the Object Pointer passed as the first parameter. The implementation class is obtained from the Object Pointer.

The implementation class may only access its own member variables, so all member variables must be private.

Instance
The instance class is instantiated to represent the class and its inheritance structure. It contains an implementation class member variable for each inherited class.

Interface
The Interface struct acts like a VTable to allow functions to be called. However it is implemented as a struct so that the function pointers may refer to renamed functions.

The offset member variable allows the implementation class instance to be obtained from the object pointer.

Identity
The identity class implements all the inheritance structure and allows it to be accessed from another project.

It sets up the VTable interfaces, and allows them to be retrieved. It also implements the "router" functions.

Links

 * Symbolic Logic:Programming:Logic Application
 * Symbolic Logic:Programming
 * Intelligence and Reasoning