text:
# Enhancing Usefulness of Declarative Programming Frameworks through Complete Integration

## 1 Introduction

Today, the difference in availability and quality of tools and libraries aimed at declarative programming languages compared to what exist for, e.g., Java$`^{\text{TM}}`$ is striking, to say the least. The non-declarative languages are often *good enough*, and the presence of mature and extensive libraries and a variety of development tools simply outweigh the advantages of using a declarative approach. Until the declarative languages manage to close the gap, motivating the use of a declarative language for general purpose, large scale programming projects is hard, even though the language might have many desirable properties for the task at hand.

An alternative approach then, is to make use of all the development years put into legacy programming tools, and to combine these with declarative programming. Thus, each programming paradigm can be used for the task it does best. In this manner, knowledge representation, reasoning, and other inherently declarative activities can be programmed in a natural high-level declarative way, and graphical user interfaces (guis), network communication, or database access using other techniques. A problem with this integration of different paradigms and tools is that connecting the different parts of a system often is rather complicated, again lessening the chance that declarative languages really become used in real-world interactive applications.

In the Gisela project, we have taken this integrative approach to making declarative programming more useful in the development of real-world applications with guis. However, instead of providing some kind of foreign-language interface, we have developed a system and methodology for *complete integration* of the different programming paradigms used. Accordingly, Gisela is not intended to be yet another declarative programming language, but rather a general *framework* for building embedded declarative reasoning components within applications. As such Gisela provides:

1. A declarative computation model based on an abstract notion of a definition
2. An object-oriented framework providing two different apis: programming using objects and ‘traditional’ equational declarative programming
3. The possibility to *experiment* with definitional programming, due to a general description of computations and the possibility to introduce new classes into the object-oriented framework or subclass existing ones.

The result is a seamless integration with an object-oriented programming environment and classes for development of desktop and web applications, which is open for modifications and extensions through such concepts as inheritance and subclassing. From the point of view of systems development, this is just as important as seeking the perfect computation model or fastest implementation.

Gisela evolved out of the need for a declarative programming tool suitable for the development of real-world applications, and it has been used in the development of a knowledge-based system in the area of oral medicine .

The rest of this paper is organized as follows. In Sect. 2, the declarative model of Gisela is outlined and an example using traditional declarative programming is given. Section 3 explains how declarative and object-oriented programming are used together. Section 4 gives a few example applications. The paper is concluded in Sect. 5 with a general discussion.

## 2 Definitional Programming

A common concept of declarative programming is the concept of a *definition*. Function definitions are given, predicates are defined etc. However, focus is on what we define, on the functions and predicates respectively. *Definitional programming* is an approach to declarative programming where the definition is the basic notion, i.e., focus is on the definitions themselves, not on what we define.

In this section we outline the model for computing with definitions, which forms the basis of Gisela. A complete presentation is given in .

### 2.1 Definitions

The definitional model of Gisela can be seen as an extension of logic programming based on the theory of *partial inductive definitions* . In this theory, a definition $`D`$ is given by

1. two sets: the *domain* of $`D`$, written $`\mathrm{dom}(D)`$, and the *co-domain* of $`D`$, written $`\mathrm{com}(D)`$, where $`\mathrm{dom}(D)\mathrm{com}(D)`$,
2. and a *definiens* operation: $`D:\mathrm{com}(D)𝒫(\mathrm{com}(D))`$.

Objects in $`\mathrm{dom}(D)`$ are called *atoms* and objects in $`\mathrm{com}(D)`$ are called *conditions*.

A natural presentation of a definition $`D`$ is that of a system of equations

$$\begin{array}{ccc}D\hfill & \{\begin{array}{ccc}a_0\hfill & =& A_0\hfill \\ & \mathrm{}& \\ a_n\hfill & =& A_n\hfill \\ & \mathrm{}& \end{array}& n0,\hfill \end{array}$$

where $`a_0,\mathrm{}\mathrm{dom}(D)`$ and $`A_0,\mathrm{}\mathrm{com}(D)`$, i.e., all pairs $`(a_i,A_i)`$ such that $`a_i\mathrm{dom}(D)`$ and $`A_iD(a_i)`$. Note that an equation $`a=A`$ is just a notation for $`A`$ being a member of $`D(a)`$.

### 2.2 Programs and Computations

Programs consist of *data definitions*, *method definitions*, and *state definitions*.

Data definitions describe the declarative content of a program. As an example, pure Prolog is a subset of definitional programming , and using the simple interactive system of Gisela (see Sect. 4.1), a data definition `permutation` with two Prolog-style predicates can be given:

```
definition permutation.

perm([],[]).
perm([X|Xs],[Y|Ys]) = select(Y,[X|Xs],Zs), perm(Zs,Ys).

select(X,[X|Xs],Xs).
select(Y,[X|Xs],[X|Ys]) = select(Y,Xs,Ys).

```

A computation is a transformation of an initial state definition into a final state definition (referred to as a *result definition*). In addition to the result definition, the result of a computation contains an answer substitution for variables in the initial state definition. Result definitions contain information about how they were computed. If we are not interested in the complete structure of a result definition, it can be simplified in different ways.

In this example, the result definition is of no particular interest so the system is set to display answer substitutions only:

```
G3> restype(vars_only).

```

Method definitions describe algorithms, or search strategies, used to compute solutions. The method definitions give the computation steps that transform the initial state definition into the result definition.

Continuing with the example, we define a method definition `prolog`:

```
method prolog(P).

prolog = [] # some r:matches(true).
prolog = [prolog, r:P] # all not(r:matches(true)).

```

Parameterized with the data definition `P`, `prolog` computes on the right-hand side of the state definition as long as the right-hand side does not equal `true`. When the right-hand side equals `true` the computation stops.

A *query* is the application of a method definition to an initial state definition. To get a Prolog query, the initial state definition is set up with the logic programming goal as the single right-hand side. In this example, we instantiate `prolog` with `permutation` and apply the result to `{true = perm([a,b,c],L)}`:

```
G3> prolog(permutation){true = perm([a,b,c],L)}.
L = [a,b,c] ? ;
L = [a,c,b] ?
yes

```

The operational semantics is given by a calculus , in which the inference rules interpret the conditions used in method definitions. The computation model has to handle certain choices, e.g., the order in which equations should be considered, the ordering of the elements of the definiens, and how to transform result definitions. These choices are handled by an *observer*. The default observer considers equations in the order they are defined in the program and allows three types of transformations of the result definitions. In this way, the notion of an observer provides a ‘hook’ into the computation model.

Logic programming is but one way to use definitional programming. Other techniques are studying properties of definitions, such as similarity  and separated definitional programming .

## 3 Complete Integration

Gisela was developed with the specific aim of providing a tool that would be useful for knowledge representation and reasoning within real-world, state-of-the art, desktop and web applications. Due to the presence of very good object-oriented tools for application development it was decided that the most practical approach would be to provide for a seamless integration between these tools and Gisela instead of trying to build a definitional gui programming library.

Gisela is currently integrated with the OpenStep framework , which means that Gisela is implemented in Objective-C, an object-oriented extension to ansi C. For the future, we are considering porting Gisela to a more widespread platform, e.g., Java$`^{\text{TM}}`$, for the obvious reason of greater platform independence.

Gisela adheres to the common use of *libraries* (*frameworks* in OpenStep, *packages* in Java$`^{\text{TM}}`$) for providing and encapsulating all the functionality for a certain task. A framework should be complete enough to be used as is, but also flexible enough to allow modification through subclassing. Building an application in a framework-based environment is just to provide the application specific details, all the general machinery and design patterns are set up by the frameworks used.

From this perspective, the aim of Gisela is to be just another framework, a framework providing all the necessary tools for embedding definitional programming components into applications. The situation can be compared to the use of a relational database in an application: In such an application, a database framework is used to seamlessly connect the application to an existing database engine using classes in the framework. All the details of, e.g., sql, is handled by the framework, and if needed the framework can be extended through subclassing. In an application using Gisela, a declarative subsystem can be integrated through the use of classes in the framework, extending them if necessary. Furthermore, a traditional equational api can be used to program the declarative part if desired, and the framework provides everything that is necessary to load and execute the definitional part at runtime.

### 3.1 The Gisela Framework

In principle, Gisela represents each entity involved in a computation by an object of a corresponding class. Thus, an equation is represented by an object of the `DFEquation` class and so on. Additionally, the framework defines a number of interfaces (*protocols* in Objective-C), which declare the functionality of each kind of object. Thus, apart from subclassing, it is possible to replace classes by new ones implementing the required interfaces.

Internally, Gisela consists of three frameworks: `DFDefinitions`, in which data definitions are implemented, `DFMethods`, which implements all classes needed to construct method definitions, and `DFComputing`, which implements the classes for performing computations. The separation enables definition classes to used by their own and keeps the frameworks reasonably sized.

#### 3.1.1 Computing Machinery.

The heart of the Gisela framework is the *D-Machine*, implemented by the `DFDMachine` class. The D-Machine performs the actual computations by implementing the calculus in the underlying definitional model. The D-Machine only relies on a few well-defined operations on data and method definitions, opening up for different ways to realize these parts.

The most important methods of the D-Machine interface are: *init*(*Delegate**Observer*), *setQuery*(*Query*), *nextAnswer*, and *allAnswers*.

A D-Machine may be set up in different ways depending on the context. The machine can run in the same thread as the object creating it or in a separate thread, which might be more appropriate for interactive applications. The machine’s observer can be any object implementing the appropriate methods. The delegate is an object which handles certain things for the machine and receives notifications at times. It can be the same as the observer or another object.

The observer interface declares methods which provide hooks into the D-Machine (see Sect. 2.2), e.g., *transformResult*(*ResultDefinition**ResultType*).

#### 3.1.2 Data Definitions.

All data definition classes must implement the methods in the `DFDefinition` interface, of which the most important are: *inDom*(*Object*) and *inCom*(*Object*), which check if *Object* is a member of the domain or co-domain respectively, and *def*(*Object*), which computes the definiens of *Object*.

For a data definition class to be valid, the definitions of the above methods should implement the behavior given by the abstract description of a data definition given in Sect. 2.1.

The D-Machine treats data definitions as black boxes. All it knows about data definitions is that a data definition can be used to find the definiens of an object and that there may, in general, be more than one result.

In the general case, computing the definiens of an object with respect to a data definition is a complex operation. Therefore, the framework provides several specialized data definition classes handling various simpler definitions. The most common classes are `DFModifiableDefinition`, which implements common behavior of mutable definitions, and `DFMatchingDefinition`, which is suitable when matching, and not unification, should be applied in the definiens operation.

#### 3.1.3 Method Definitions.

What is special about method definitions is that the definiens operation is always performed with respect to a given state definition. Therefore, the class `DFMethod` (a subclass of `DFModifiableDefinition`) also declares a method *defWithStateDefinition*(*StateDefinition*).

As with other entities involved in computations, users using the Gisela frameworks may implement new method definition classes as long as they implement the `DFMethod` interface. Of course, subclassing is also possible.

### 3.2 Building an Application

From Gisela’s point of view, an application consists of the application binary and a resources folder, the latter containing all sorts of resources needed by the application. The items in the resources folder can dynamically be loaded into the application at run time. This means that it is straightforward to have text files in the resources folder representing definitions and computation methods. These text files can be parsed into definition objects at run time using the api provided by the Gisela framework. Consequently, any Gisela program developed using equational presentations can smoothly be integrated into an application.

#### 3.2.1 Model–View–Controller.

The general design approach used when integrating Gisela into an application is the Model–View–Controller (mvc) paradigm. mvc is a commonly used object-oriented software development methodology. When mvc is used, the *model*, i.e., data and operations on data, is kept strictly separated from the *view* displayed to the user. The *controller* connects the two, and decides how to handle user actions and how data obtained from the model should be presented in the view. Applied to the Gisela setting, the model of what an application should do is implemented using definitional programming in Gisela. The view displayed to the user can be of different kinds, desktop applications, web applications etc. In between the view presented to the user and the Gisela machinery there is a controller object, which manages communication between the two parts. One advantage of this approach is, of course, that different views may be used without changing the model.

The general methodology to use Gisela to build applications thus becomes:

* Decide what definitions are needed for the definitional part of the application.
* Write the syntactic representations of the Gisela program part and add the resulting files to the application’s resources.
* At run time, load the definitional resources into objects representing them and create the desired number of `DFDMachine` objects for running queries. A `DFDMachine` object can be set up to interact with the application in various ways, depending on the demands of the application.
* Build a `DFQuery` object, representing the query, from user input somehow.
* Ask a `DFDMachine` to run the query represented by the `DFQuery` object.
* Present the result, represented by a `DFAnswer` object, to the user somehow.

The Gisela approach is in line with the Model-View-Controller paradigm, where Gisela is used to build the model, and the controller and view are constructed using standard programming tools and available libraries. Typically, when a new model object is created it allocates a `DFDMachine` object and loads the required definitional computation resources contained within the application.

### 3.3 Extending Gisela’s Capabilities

We show a couple of examples of how the Gisela framework can be modified or extended by developers using it to build applications.

#### 3.3.1 A Simple Database Adaptor.

In the MedView project , results from a large number of examinations are stored as text files in a knowledge base. The conceptual view of the knowledge base is that of a collection of definitions. However, the format of the files is not one that can be directly parsed by any standard definition class. Thus, to be able to manipulate MedView data, a first step is to extend the framework with a new definition class, called `MVTreeDefinition`, which understands the used format.

From the D-Machine’s point of view, objects of the `MVTreeDefinition` class are no different from, e.g., definitions created from equational presentations like `permutation` shown in Sect. 2.

#### 3.3.2 Another Observer.

The observer is responsible for selecting the order in which equations are considered. If we want to consider the left-most equation only, we can introduce a new observer class. In this class we override the appropriate method from the default observer:

```
@implementation LeftMostObserver
- (NSArray *)selectEquationsWithWord:(DFWord *)aWord
                     stateDefinition:(DFStateDefinition *)stateDef
                            andHints:(NSArray *)hints {
     return [NSArray arrayWithObject:[NSNumber numberWithInt:0]];
}
@end

```

The left-most equation is the one at index `0`.

The power of object-oriented programming in general, and inheritance in particular, lets us experiment with definitional computations using the Gisela framework as a basis.

## 4 Example Applications

In this section, we illustrate the use of Gisela with a few examples built using the integrative approach to application development.

To further increase the usefulness of Gisela, some basic tools supporting application development have been built. The development of the tools themselves is based on the use of the Gisela framework in combination with object-oriented programming to handle user interaction.

### 4.1 A Simple ide

Using the object-oriented apis of Gisela and following the mvc paradigm, we have written a simple interactive system useful for developing and testing definitional programs in a traditional way.

However, nowadays, one typically uses an ide for management of code, application resources, debugging etc., rather than just a text editor and a shell. In the world of imperative and object-oriented programming, there exists a large number of high-quality ides. For declarative programming, ides are scarce.

To simplify development of equational definitional programs, a basic ide is being developed. The general principle in the development of the ide is the same as in application development in general: Object-oriented programming is used to build the gui, and this is then hooked up to Gisela to manage definitions.

The ide is centered around the notion of a project. A project consists of a number of related data and method definitions. The main window consists of a toolbar with common commands at the top, an outline view showing all the project’s files to the left, and a main area to the right for editing source code etc. The main area can be split into several views for different tasks, e.g., running queries from within the ide. To parse a data or method definition, classes provided by the framework are used. To run a query, a `GiselaRunTime` object is used, however hooked up to a gui rather than a command line interface as above. The user can easily control properties of the D-Machine using pop-ups.

Although rather incomplete, the ide has been designed to be extendable. For example, once a debugger has been developed it can easily be added.

### 4.2 MedViewer – A Real Application

MedViewer is an application for dynamic data exploration developed within the MedView project. The user dynamically selects cases from a knowledge base containing about 3000 patient examinations. The selected cases are then visualized in different ways, e.g., using scatter plots or bar charts (see Fig. 1).

MedViewer uses the Gisela framework in several ways. All data in the knowledge base is represented by `MVTreeDefinition` objects. To make selections, definitional computations are used, based on method definitions stored within the application. The user can also construct groups of related cases. These groups are data definitions developed by users using a special gui. Finally, to generate case descriptions, data definitions are used to represent text templates, using some custom data definition classes. The text generator does not use a D-Machine, but hard-wired procedural behavior, yet another way of using Gisela.

## 5 Discussion

Our experiences from using Gisela are positive: It provides seamless integration with tools for building desktop and web applications, and can easily be modified or extended when functionality not present in the framework is needed. If desired, the framework can also be used for declarative programming at different levels. For instance, it is possible to use only data definitions describing a domain and implement the procedural behavior without using Gisela.

Gisela has been evaluated in practice within the MedView project. Applications for knowledge acquisition, data presentation using natural language generation, and information visualization have been developed. These tools are in daily use at several clinics attached to somnet (Swedish Oral Medicine NETwork).

There are two things that set Gisela apart from most approaches to declarative programming: (i) Gisela is not a general-purpose programming language, rather it is a system for realizing a certain set of definitional models and (ii) Gisela is a framework with an abstract definition, specifically aimed at allowing experiments and modifications within the general definitional and computational models. Furthermore Gisela is designed for complete integration into a surrounding object-oriented environment.

There are similar realizations of declarative languages, typically targeting Java$`^{\text{TM}}`$. For instance, Frappé  that implements the abstract concept of Functional Reactive Programming  in Java$`^{\text{TM}}`$ in a way related to how Gisela applications can be developed using the object-oriented api. Another is tuProlog , which implements Prolog in Java$`^{\text{TM}}`$ in a manner enabling a tight integration between the two languages. However, contrary to Gisela the declarative part is a fixed programming language instead of an extendable framework.

An alternative to mixing declarative programming with other tools to build guis is to provide declarative libraries, e.g., . However, we have not seen any examples that are on par with their object-oriented counterparts in terms of ease-of programming, development tools, or quality of the resulting interface. When it comes to interfacing declarative system with other tools there are several choices. Most declarative languages have foreign language interfaces, but these are often low-level and rather cumbersome to use. A more high-level generic interface is shown in . Another way to achieve integration, perhaps more related to Gisela, is to compile into a common platform providing integration facilities. An example of this is the use of the .net common language runtime as target for Mercury . There are also several systems that compile to Java$`^{\text{TM}}`$ bytecode, such as MLj . At another level is providing and using interfaces for standardized techniques for building component based systems such as com/corba, e.g., .

The complete integration approach used in Gisela differs from all of the above in that it does not require any kind of interface to the system it is integrated into. There is a price to pay though: The complete integration is with one particular object-oriented application development environment, currently OpenStep - to integrate with another one a new implementation is required.

Currently, we have no plans to extend Gisela to handle sophisticated interaction with the user. Instead, we advocate the integrated approach with a definitional model realized in Gisela and an interface part programmed using other, more suitable, tools. Thus, the declarative framework, in effect, extends the entire application development framework surrounding it. In our experience, this is the most practical approach. Integration is one one crucial factor for the success of declarative programming. Another factor is making a shift from the development of programming languages to systems development: In studying object-oriented system development, learning a language like Java$`^{\text{TM}}`$ is but a small part, learning modelling languages and design patterns is equally important. Gisela is our attempt at providing a useful declarative programming component.