Conference PaperPDF Available

An Overview of the SWI-Prolog Programming Environment.

Authors:

Abstract and Figures

The Prolog programmer's needs have always been the focus for guiding the development of the SWI-Prolog system. This article ac- companies an invited talk about how the SWI-Prolog environment helps the Prolog programmer solve common problems. It describes the central parts of the graphical development environment as well as the command line tools which we see as vital to the success of the system. We hope this comprehensive overview of particularly useful features will both in- spire other Prolog developers, and help SWI-Prolog users to make more productive use of the system.
Content may be subject to copyright.
Proceedings of the 13th International
Workshop on Logic Programming
Environments
Fred Mesnard
Alexander Serebrenik (Eds.)
Report CW 371, November 2003
Katholieke Universiteit Leuven
Department of Computer Science
Celestijnenlaan 200A – B-3001 Heverlee (Belgium)
i
Proceedings of the 13th International
Workshop on Logic Programming
Environments
Fred Mesnard
Alexander Serebrenik (Eds.)
Report CW 371, November 2003
Department of Computer Science, K.U.Leuven
ii
Preface
This volume contains papers presented at WLPE 2003, the 13th Interna-
tional Workshop on Logic Programming Environments. The aim of WLPE is to
provide an informal meeting for researchers working on tools for development
and analysis of logic programming. This year, the emphasis is on the presenta-
tion, pragmatics and experiences of such tools.
WLPE 2003 takes place in Tata Institute of Fundamental Research, Mum-
bai, India on December 8 and is a part of a bigger event, ICLP 2003, the 19th
International Conference on Logic Programming, holding in conjunction with
ASIAN 2003, the Eighth Asian Computing Science Conference, and FSTTCS
2003, the 23rd Conference on Foundations of Software Technology and Theor-
etical Computer Science. This workshop continues the series of successful in-
ternational workshops on logic programming environments held in Ohio, USA
(1989), Eilat, Israel (1990), Paris, France (1991), Washington, USA (1992),
Vancouver, Canada (1993), Santa Margherita Ligure, Italy (1994), Portland,
USA (1995), Leuven, Belgium and Port Jefferson, USA (1997), Las Cruces,
USA (1999), Paphos, Cyprus (2001) and Copenhagen, Denmark (2002).
We would like to express our gratitude to the ICLP organisers for hosting the
workshop. Special thanks go to R.K.Shyamasundar for taking care of the many
organisational matters, in particular, printing these proceedings. Also we would
like to thank the program committee members for reviewing and discussing the
submissions as well as the authors for submitting their work.
Out of 9 submissions the program committee has selected 5 works for present-
ation. In addition, Jan Wielemaker (University of Amsterdam, The Netherlands)
was invited to present a number of typical problems Prolog users are faced with
and illustrate how tools developed in SWI-Prolog may help to find them.
Fred Mesnard
Alexander Serebrenik
Mumbai, December 2003
i
Organisation
13th Workshop on Logic Programming Environments
WLPE 2003 December 8, 2003, Mumbai, India
Workshop organisers:
Fred Mesnard (Universit´
e de La R´
eunion, France)
Alexander Serebrenik (coordinator, Katholieke Universiteit Leuven, Bel-
gium)
Program committee:
Roberto Bagnara (Universit`
a degli studi di Parma, Italy)
Manuel Carro (Universidad Polit´
ecnica de Madrid, Spain)
Mireille Ducass´
e (INSA/IRISA, Rennes, France)
Pat Hill (University of Leeds, U.K.)
Naomi Lindenstrauss (Hebrew University of Jerusalem, Israel)
Jan-Georg Smaus (Universit¨
at Freiburg, Germany)
Fausto Spoto (Universit`
a di Verona, Italy)
Alexandre Tessier (Universit´
e d’Orl´
eans, France)
Reviewers:
Pieter Bekaert
Maurice Bruynooghe
Daniel Cabeza
Pierre Deransart
Gerard Ferrand
Jos´
e Manuel G´
omez
Arnaud Lallouet
Tom Schrijvers
Zoltan Somogyi
Joost Vennekens
ii
Table of Contents
An Overview of the SWI-Prolog Programming Environment ........... 1
Jan Wielemaker
TCLP: A type checker for CLP(X)................................ 17
Emmanuel Coquery
Analyzing and Visualising Prolog programs based on XML representations 31
Dietmar Seipel, Marbod Hopfner, Bernd Heumesser
Demonstration proposal: Debugging constraint problems with portable tools 46
Pierre Deransart, Ludovic Langevine and Mireille Ducasse
Proving Termination One Loop at a Time .......................... 48
Michael Codish and Samir Genaim
Hasta-La-Vista: Termination Analyser for Logic Programs ............. 60
Alexander Serebrenik and Danny De Schreye
Constructive combination of crisp and fuzzy logic in a Prolog compiler . . 75
Susana Munoz, Claudio Vaucheret and Sergio Guadarrama
iii
An Overview of the SWI-Prolog Programming
Environment
Jan Wielemaker
Social Science Informatics (SWI),
University of Amsterdam,
Roetersstraat 15, 1018 WB Amsterdam, The Netherlands,
jan@swi.psy.uva.nl
Abstract. The Prolog programmer’s needs have always been the focus
for guiding the development of the SWI-Prolog system. This article ac-
companies an invited talk about how the SWI-Prolog environment helps
the Prolog programmer solve common problems. It describes the central
parts of the graphical development environment as well as the command
line tools which we see as vital to the success of the system. We hope
this comprehensive overview of particularly useful features will both in-
spire other Prolog developers, and help SWI-Prolog users to make more
productive use of the system.
1 Introduction
SWI-Prolog has become a popular Free Software implementation of the
Prolog language. Distributed freely through the internet, it is difficult to
get a clear picture about its users, how these users use the system and
which aspects of the system have contributed most to its popularity. Part
of the users claim the programmer’s environment described in this article
is an important factor.
The majority of the SWI-Prolog users are students using it for their as-
signments. The community of developers, however, expend effort on large
portable Prolog applications where scalability, (user-) interfaces, network-
ing are often important characteristics. Compared to the students, who
are mostly short-term novice users, we find many expert software de-
velopers in the research and development community.
The material described in this paper is the result of about 18 years
experience as a Prolog programmer and developer of the SWI-Prolog
system. Many of the described tools are features not unique to SWI-Prolog
and can be found in other Prolog implementations or other programming
language environments. Experiments are yet to be performed to evaluate
the usefulness of features and therefore the opinions presented are strictly
based on our own experiences, observations of users, and E-mail reactions.
1
After describing the SWI-Prolog user community in Sect. 2 we de-
scribe some problems Prolog programmers frequently encounter in Sect. 3.
In Sect. 4 we describe the command line tools, and in Sect. 5 the graphical
tools written in SWI-Prolog’s XPCE GUI toolkit [10].
2 User profiles
Students having to complete assignments for a Prolog course have very
different needs from professionals developing large systems. They want
easy access to common tasks as closely as possible to the conventions
they are used to. Scalability of supporting tools is not an important
issue as the programs do not require many resources. Visualization of
terms and program state can concentrate their contribution to explan-
ation and disregard, for example, the issue that most graphical repres-
entations scale poorly. The SWI-Prolog-Editor1shell for MS-Windows by
Gerhard R¨ohner makes SWI-Prolog much more natural to a student who
is first of all familar with MS-Windows.
SWI-Prolog comes from the Unix and Emacs tradition and targets the
professional programmer who uses it frequently to develop large Prolog-
based applications. As many users in this category have their existing
habits, and a preferred set of tools to support these, SWI-Prolog avoids
presenting a single comprehensive IDE (Integrated Development Environ-
ment), but instead provides individual components that can be combined
and customised at will.
3 Problems
Many problems that apply to programming in Prolog also relate the pro-
gramming in other languages. Some, however, are Prolog specific. Prolog
environments can normally be used interactively and changed dynamic-
ally.
3.1 Problem areas
Managing sources
Besides the normal problems such as locating functions and files, Pro-
log requires a tool that manages consistency between the sources and
running executable during the interactive test-edit cycle. Section 4.1
and Sect. 5.1 describe the SWI-Prolog support to manage sources.
1http://www.bildung.hessen.de/abereich/inform/skii/material/swing/indexe.htm
2
Entering and reusing queries
Interaction through the Prolog top level is vital for managing the pro-
gram and testing individual predicates. Command line editing, com-
mand completion, do what I mean (DWIM) correction, history, and
storing the values of top level variables reduces typing and speed up
the development cycle.
Program completeness and consistency
SWI-Prolog has no tradition in rigid static analysis. It does provide a
quick completeness test as described in Sect. 4.6 which runs automat-
ically during the test-edit cycle. A cross-referencer is integrated into
the built-in editor (Sect. 5.1) and provides immediate feedback to the
programmer about common mistakes while editing a program.
Error context
If an error occurs, it is extremely important to provide as much context
as possible. The SWI-Prolog exception handling differs slightly from
the ISO standard to improve such support. See Sect. 4.10.
Failure/wrong answer
A very common and time consuming problem are programs producing
the wrong (unexpected) answer without producing an error. Although
research has been carried out to attribute failure and wrong answers
to specific procedures [3, 9], none of this is realised in SWI-Prolog.
Determinism
Although experience and discipline help, controlling determinism in
Prolog programs to get all intended solutions quickly is a very common
problem. The source-level debugger (Sect. 5.3) displays choicepoints
and provides immediate graphical feedback on the effects of the cut,
greatly simplifying this task and improving understanding for novices.
Performance bottlenecks
Being a high level language, the relation between Prolog code and
required resources to execute it is not trivial. Profiling tools cannot fix
poor overall design, but do provide invaluable insight to programmer.
See Sect. 5.4.
Porting programs from other systems
Porting Prolog programs has been simplified since more Prolog sys-
tems have adopted part I of the ISO standard. Different extensions
and libraries cause many of the remaining problems. Compiler warn-
ings and static analysis form the most important tools to locate the
problem areas quickly. A good debugger providing context on errors
together with support for the test-edit cycle improve productivity.
3
4 Command line Tools
4.1 Supporting the edit cycle
Prolog systems offer the possibility to interactively edit and reload a pro-
gram even while the program is running. There are two simple but very
frequent tasks involved in the edit-reload cycle: finding the proper source,
and reloading the modified source files. SWI-Prolog supports these tasks
with two predicates:
make
SWI-Prolog maintains a database of all loaded files with the file
last-modified time stamp when it was loaded and —for the sake of
modules— the context module(s) from which the file was loaded. The
make/0 predicate checks whether the modification time of any of the
loaded files has changed and reload these file into the proper module
context. This predicate has proven to be very useful.
edit(+Specifier)
Find all entities with the given specifier. If there are multiple entities
related to different source-files ask the user for the desired one and
call the user-defined editor on the given location. All entities implies
(loaded) files, predicates and modules. Both locating named entities
and what is required to call the editor on a specific file and line can
be hooked to accomodate extensions (e.g. XPCE classes) and differ-
ent editors. Furthermore, SWI-Prolog maintains file and line-number
information for modules and clauses. Below is an example:
?- edit(rdf_tree).
Please select item to edit:
1 class(rdf_tree) ’rdf_tree.pl’:27
2 module(rdf_tree) ’rules.pl’:460
Your choice? 2
SWI-Prolog’s completion and DWIM described in Sect. 4.4 and
Sect. 4.3 improve the usefulness of these primitives.
4.2 Autoloading and auto import
Programmers tend to be better at remembering the names of library pre-
dicates than the exact library they belong to. Similar, programmers of
4
large modular applications often have a set of personal favourites and
application specific goodies. SWI-Prolog supports this style of program-
ming with two mechanisms, both of which require a module system. The
SWI-Prolog module system is very close to the Quintus and SICStus Pro-
log module systems [2].
Auto import tries to import undefined predicates from the module’s
import module. The module system contains all built-in predicates, user
all global predicates and all other modules import from user as illustrated
in Fig. 1. This setup allows programmers to define or import commonly
used predicates into user and have them available without further actions
from the interactive top level and all modules.
System
User
System
Module 1
System
Module 2
System
Module-N
User
Module 1
User
Module 2
User
Module-N
Fig. 1. Modules and their auto-import relations
Library auto loading avoids the need for explicit use module/[1,2]
declarations. Whenever the system encounters an unknown predicate it
examines the library index. If the predicate appears in the index the
library is loaded using use module/2, only importing the missing pre-
dicate.
The combination of auto import, auto loading and a structuring mod-
ule system has proven to support both sloppy programming for rapid pro-
totyping and the use of more maintainable explicit module relations. The
predicate list autoload/0 as described in Sect. 4.6 supports a smooth
transition.
4.3 DWIM: Do What I Mean
DWIM (Do What I Mean) is implemented at the top level to quickly fix
mistakes and allow for underspecified queries. It corrects the following
errors:
5
Simple spelling errors
DWIM checks for missing, extra and transposed characters that result
from typing errors.
Word breaks and order
DWIM checks for multi-word identifiers using different conventions
(e.g. fileExists vs. file exists) as well as different order (e.g. exists file
vs. file exists)
Arity mismatch
Of course such errors cannot be corrected.
Wrong module
DWIM adds a module specification to predicate references that lack
one or replaces a wrong module specification.
DWIM is used in three areas. Queries typed at the top level are
checked and if there is a unique correction the system prompts whether
to execute the corrected rather than the typed query. Especially adding
the module specifier improves interaction from the top level when using
modules. If there is no unique correction the system reports the missing
predicates and all close candidates. Queries of the development system
such as edit/1 and spy/1 provide alternative matches one-by-one. Spy/1
and trace/1 act on the specified predicate in any module if the module
is omitted. Finally, if a predicate existence error reaches the top level the
DWIM system is activated to report likely candidates.
4.4 Command line editing
Developers spend a lot of time entering commands for the development
system and (test-)queries for (parts of) their application under develop-
ment. SWI-Prolog provides the following features to support this:
Using (GNU-)readline
Emacs-style editing is supported in the Unix version based on the
GNU readline library and in Windows using our own code. This facil-
itates quick and natural command reuse and editing. In addition, com-
pletion is extended with completion on alphanumerical atoms which
allow for fast typing of long predicate identifiers and atom arguments
as well as inspect the possible alternative (using Alt-?). The comple-
tion algorithm uses the builtin completion of files if no atom matches,
which ensures that quoted atoms representing a file path is completed
as expected.
6
Command line history
SWI-Prolog provides a history facility that resembles the Unix csh
and bash shells. Especially viewing the list of executed commands is
a valuable feature.
Top level bindings
When working at the Prolog top level, bindings returned by previous
queries are normally lost while they are often required for further
analysis of the current Prolog state or to test further queries. For
this reason SWI-Prolog stores the resulting bindings from top level
queries, provided they are not too large (default 1000 tokens) in
the database under the name of the used variable. Top level query
expansion replaces terms of the form $Var ($ is a prefix operator)
into the last recorded binding for this variable. New bindings do to
backtracking or new queries overwrite the old value.
This feature is particularly useful to query the state of data stored in
related dynamic predicates and deal with handles provided by external
stores. Here is a typical example using XPCE that avoids typing or
copy/paste of the object reference.
?- new(X, picture).
X = @12946012
?- send($X, open).
4.5 Compiler
An important aspect of the SWI-Prolog compiler is its performance. Load-
ing the 21 Mb sources of WordNet [7] requires 6.6 seconds from the source
and 1.4 seconds from precompiled virtual machine code (Multi-threaded
SWI-Prolog 5.2.9, SuSE Linux on dual AMD 1600+ using one thread).
Fast compilation is very important during the interactive development of
large applications.
SWI-Prolog supports the commonly found set of compiler warnings:
syntax errors, singleton variables, predicate redefinition, system predicate
redefinition and discontiguous predicates. Messages are processed by the
hookable print message/2 predicate and where possible associated with
a file and line number. The graphics system contains a tool that exploits
the message hooks to create a window with error messages and warnings
that can be selected to open the associated source location.
7
4.6 Quick consistency check
The library check provides quick tests on the completeness of the loaded
program. The predicate list undefined/0 searches the internal database
for predicate structures that are undefined (i.e. have no clauses and are
not defined as dynamic or multifile). Such structures are created by the
compiler for a call to a predicate that is not yet defined. In addition the
system provides a primitive that returns the predicates referenced from a
clause by examining the compiled code. Figure 2 provides partial output
running list undefined/0 on the chat 80 [8] program:
1 ?- [library(chat)].
% ...
% library(’chat/chat’) compiled into chat 0.18 sec, 493,688 bytes
% library(chat) compiled into chat 0.18 sec, 494,756 bytes
Yes
2 ?- list_undefined.
% Scanning references for 9 possibly undefined predicates
Warning: The predicates below are not defined. If these are defined
Warning: at runtime using assert/1, use :- dynamic Name/Arity.
Warning:
Warning: chat:ditrans/12, which is referenced by
Warning: 5-th clause of chat:verb_kind/6
Fig. 2. Using list undefined/0 on chat 80 wrapped into the module chat. To save
space only the first of the 9 reported warnings is included. The processing requires
0.25 sec. on a 733 Mhz PIII.
The list autoload/0 predicate lists undefined predicates that can be
autoloaded from one of the libraries. It is illustrated in Fig. 3.
3 ?- list_autoload.
% Into module chat (library(’chat.pl’))
% display/1 from library(edinburgh)
% last/2 from library(lists)
% time/1 from library(statistics)
% Into module user
% prolog_ide/1 from library(swi_ide)
Fig. 3. Using list autoload/0 on chat 80
8
4.7 Help and explain facility
The help facility uses outdated but still effective technology. The L
A
T
E
X
maintained source is translated to plain text. A generated Prolog index
file provides character ranges for predicate descriptions and sections in
the manual. Each predicate has, besides the full documentation, a ±
40 character summary description used for apropos search as well as to
provide a summary string in the editor as illustrated in Fig. 4.
The explain facility examines the database to gather all information
known about an identifier (atom). Information displayed includes predic-
ates with that name and references to the atoms, compound terms and
predicates with the given name. Here is an example:
explain(setof).
"setof" is an atom
Referenced from 1-th clause of chat:decomp/3
system:setof/3 is a built-in meta predicate imported from module
$bags defined in
/staff/jan/lib/pl-5.2.9/boot/bags.pl:59
Summary: ‘‘Find all unique solutions to a goal’’
Referenced from 6-th clause of chat:satisfy/1
Referenced from 7-th clause of chat:satisfy/1
Referenced from 1-th clause of chat:seto/3
The graphical front end is described in Sect. 5.5.
4.8 File commands
Almost too trivial to name, but the predicates ls/0,cd/1 and pwd/0
are used very frequently.
4.9 Debugging from the terminal
SWI-Prolog comes with two tracers, a traditional 4-port debugger [1] to
be used from the terminal and a graphical source level debugger which is
described in Sect. 5.3. Less frequently seen features of the trace are:
Single keystroke operation
If the terminal supports it, commands are entered without waiting for
return.
List choicepoints
The tracer can provide a list of active choicepoints, similar to the goal
stack, to facilitate choicepoint tuning and debugging.
9
The ‘up’ command
The ‘up’ command is like the traditional ‘skip’ command, but skips
to the exit or failure of the parent goal rather than the current goal.
It is very useful to stop tracing the details of failure driven control
structures.
Search
The system can search for a specific port and goal that unifies with
an entered term. The command /f foo(_, bar) will go into inter-
active debugging if foo/2 where the second argument unifies with bar
reaches the fail (f) port.
In addition to interactive debugging two types of non-interactive de-
bugging are provided. Using trace(Predicate, Ports), the system prints
all passes to the indicated ports of Predicate.
The library debug is a lightweight infrastructure to handle printing
debugging messages (logging) and assertions. The library exploits goal-
expansion to avoid runtime overhead when compiled with optimisation
turned on. Debug messages are associated to a Topic, an arbitrary Pro-
log term used to group debug messages. Normally the Topic is an atom
denoting some function or module of the application. Using Prolog uni-
fication of the active topics and the topic registered with the message
provides opportunity for creativity.
debug(+Topic, +Format, +Arguments)
Prints a message through the system’s print message/2 message
dispatching mechanism if debugging is enabled on Topic.
debug/nodebug(+Topic)
Enable/disable messages for which Topic unifies. Note that topics are
arbitrary Prolog terms, so debug() enables all debugging messages.
list debug topics
List all registered topics and their current enable/disable setting. All
known topics are collected during compilation using goal-expansion.
assume(:Goal)
Assume that Goal can be proven. Trap the debugger if Goal fails.
This facility is derived from the C-language assert() macro defined
in <assert.h>, renamed for obvious reasons. More formal assertion
languages are described in [6, 5].
4.10 Exception context
On exception handling, the ISO standard dictates ‘undo’ back to the state
at entry of a catch/3 before unifying the ball with the catcher. SWI-
10
Prolog however uses a different technique. It walks the stack searching
for a matching catcher without undoing changes. If it finds a matching
catch/3 call or when reaching a call from foreign code that indicates
it is prepared to handle exceptions it performs the required ‘undo’ and
executes the handler. The advantage is that if there is no handler for
the exception the entire program state is still intact. The debugger is
started immediately and can be used to examine the full context of the
exception.2
5 Graphical Tools
5.1 Editor
PceEmacs is an Emacs clone written in XPCE/Prolog. It has two features
that make it of special interest. It can be programmed in Prolog and there-
fore has transparent access to the environment of the application being
developed, and the editor’s buffer can be opened as a Prolog I/O stream.
Based on these features, the level of support for Prolog development is
far beyond what can be achieved in a stand-alone editor. Whenever the
user pauses for two seconds the system performs a full cross-reference of
the editing buffer, categorising and colouring predicates, goals and gen-
eral Prolog terms. Predicates are categorised as exported,called and not
called. Goals are categorised as builtin,imported,auto-imported,locally
defined,dynamic,(direct-)recursive and undefined. Goals have a menu
that allows jumps to the source, documentation (builtin), and listing of
clauses (dynamic). Singleton variables are highlighted. If the cursor ap-
pears inside a variable all other occurrences of this variable in the clause
are underlined. Figure 4 shows a typical screenshot.
5.2 Prolog Navigator
The Prolog Navigator provides a hierarchical overview of a project direct-
ory and its Prolog files. Prolog files are categorised as one of loaded or not
loaded and are expanded to the predicates defined in them. The defined
predicates are categorised as one of exported,normal,fact and unrefer-
enced. Expanding predicates expands the call tree. The Navigator menus
provide loading and editing files and predicates as well as the setting of
trace- and spy-points. See Fig. 5.
2These issues have been discussed on the comp.lang.prolog newsgroup, April 15-18
2002, subject “ISO catch/throw question”.
11
Fig. 4. PceEmacs in action
Fig. 5. The Prolog Navigator
12
5.3 Source-level Debugger
The SWI-Prolog debugger calls a hook (prolog trace interception/4) be-
fore reverting to the built-in command line debugger. The built in pro-
log frame attribute/3 provides the infrastructure to analyse the Pro-
log stacks, providing information on the goal-stack, variable bindings and
choicepoints. These hooks are used to realise more advanced debuggers
such as the source-level debugger described in this section. The source-
level debugger provides three views (Fig. 6):
The source
An embedded PceEmacs (see Sect. 5.1) running in read-only mode
shows the current location, indicating the current port using colour
and icons. PceEmacs also allows the setting of breakpoints at a spe-
cific call in specific clause. Breakpoints provide finer and more intu-
itive control where to start the debugger than traditional spy-points.
Breakpoints are realised by replacing a virtual machine instruction
with a break instruction which traps the debugger, finds the instruc-
tion it replaces in a table and executes this instruction.
Variables
The debugger displays a list of variables appearing in the current
frame with their name and current binding in the top-left window. The
representation of values can be changed using the familiar portray/1
hook. Double-clicking a variable-value opens a separate window show-
ing the variable binding. This window uses indentation to make the
structure of the term more explicit and has a menu to control the
layout.
The stack
The top-right window shows the stack as well as the recent active
choicepoints. Any node can be selected to examine the context of that
node. The stack view allows one to quickly examine choicepoints left
after a goal succeeded. Besides showing the location of the choicepoint
itself, the ‘up’ command can be used to examine the parent frame
context of a choicepoint.
5.4 Execution Profiler
The Execution Profiler builds a call-tree at runtime and ticks the number
of calls and redos to each node in this call-tree. The time spent in each
13
Fig. 6. The Source-level Debugger
node is established using stochastic sampling.3Recording the call-tree is
complicated by three factors.
Last call optimisation
Due to last call optimisation exit ports are missing from the execution
model. This problem is solved by storing the call-tree node associated
with a goal in the environment stack, providing the exit with a ref-
erence to the node exited. Recording an exit can now exit all nodes
until it reaches the referenced node.
Redo
Having a reference from each environment frame to the call-tree node
also greatly simplifies finding the proper location in the call-tree on a
redo.
Recursion
To avoid the uncontrolled expanding of the call-tree the system must
record recursive calls. The problem lies in the definition of recursion.
The most na¨ıve definition is that recursion happens if there is a par-
ent node running the same predicate. In this view meta predicates
will often appear as unwanted ‘recursive predicates’ as will predicates
called in a totally different context. The system provides noprofile/1
to indicate some predicates do not create a new node and their time is
included with their parent node. Examples are call/1,catch/3 and
call cleanup/2. Calls are now regarded recursive if the parent node
3Using SIGPROF on Unix and using a separate thread and a multi-media timer in
MS-Windows.
14
runs the same predicate (direct recursion) or somewhere in the parent
nodes of the call-tree we can find a node running the same predicate
with the same immediate parent.
Prolog primitives are provided to extract all information from the re-
corded call-tree. A graphical Prolog profiling tool presents the information
interactively similar to the GNU gprof [4] tool (see Fig. 7).
Fig. 7. The Profiler
5.5 Help System
The GUI front end to the help functionality described in Sect. 4.7 adds
hyperlinks and hierarchical context to the command line version as illus-
trated in Fig. 8.
Fig. 8. Graphical front end to the help system
6 Conclusions
In this paper we have described commonly encountered tasks which Pro-
log programmers spend much of their time on, which tools can help solv-
15
ing them as well as an overview of the programming environment tools
provided by SWI-Prolog. Few of these tools are unique to SWI-Prolog
or very advanced. The popularity of the environment can possibly be
explained by being complete, open, portable, scalable and free.
Acknowledgements
XPCE/SWI-Prolog is a Free Software project which, by its nature, profits
heavily from user feedback and participation. We would like to thank
Steve Moyle and Anjo Anjewierden for their comments on draft versions
of this paper.
References
1. Lawrence Byrd. Understanding the control flow of Prolog programs. In S.-A.
Tarnlund, editor, Proceedings of the Logic Programming Workshop, pages 127–138,
1980.
2. M. Carlsson, J. Wid´en, J. Andersson, S. Anderson, K. Boortz, H. Nilson, and
T. Sj¨oland. SICStus Prolog (v3) Users’s Manual. SICS, PO Box 1263, S-164 28
Kista, Sweden, 1995.
3. Mireille Ducass´e. Analysis of failing Prolog executions. In Workshop on Logic
Programming Environments, pages 2–9, 1991.
4. Susan L. Graham, Peter B. Kessler, and Marshall K. McKusick. gprof: a call
graph execution profiler. In SIGPLAN Symposium on Compiler Construction,
pages 120–126, 1982.
5. M. Hermenegildo, G. Puebla, and F. Bueno. Using global analysis, partial spe-
cifications, and an extensible assertion language for program validation and debug-
ging. In The Logic Programming Paradigm: a 25-Year Perspective, pages 161–192.
Springer-Verlag, 1999.
6. Marija Kulas. Debugging Prolog using annotations. In Mireille Ducass´e, Anthony
Kusalik, and German Puebla, editors, Electronic Notes in Theoretical Computer
Science, volume 30. Elsevier, 2000.
7. G. Miller. WordNet: A lexical database for English. Comm. ACM, 38(11), Novem-
ber 1995.
8. Fernando C. N. Pereira and Stuart M. Shieber. Prolog and Natural-Language
Analysis. Number 10 in CSLI Lecture Notes. Center for the Study of Language
and Information, Stanford, California, 1987. Distributed by Chicago University
Press.
9. E. Y. Shapiro. Algorithmic Program Debugging. MIT Press, Cambridge, MA, 1983.
10. Jan Wielemaker and Anjo Anjewierden. An architecture for making object-oriented
systems available from Prolog. In Alexandre Tessier, editor, Computer Science,
abstract, 2002. http://lanl.arxiv.org/abs/cs.SE/0207053.
16
TCLP: A type checker for CLP(X)
Emmanuel Coquery
Emmanuel.Coquery@inria.fr
November 7, 2003
Abstract
This paper is a presentation of TCLP: a prescriptive type checker for
Prolog/CLP(X). Using parametric polymorphism, subtyping and overload-
ing, TCLP can be used with practical constraint logic programs that may use
meta-programming predicates, coercions between constraint domains (like FD
and B) and constraint solver definitions, including the CHR language. It also
features type inference for variables and predicates, so the user can get rid of
numerous type declarations.
1 Introduction
Traditionally, the class CLP(X) of constraint logic programs, introduced
by Jaffar and Lassez [11], is untyped. One of the advantages of being
untyped is programming flexibility. For example, -/2 can be used as
the classical arithmetic operator as well as a constructor for pairs. On
the other hand, type checking allows the static detection of some pro-
gramming errors, like for example calling a predicate with an illegal
argument.
Several type systems have been created for (constraint) logic pro-
gramming. The type system of Mycroft and O’Keefe [12, 15] is an adap-
tation of the Damas-Milner type system [6] to logic programming. It
has been implemented in G¨odel [10] and Mercury [19]. This type system
uses parametric polymorphism, that is, parameters (i.e. type variables)
are allowed as and in types. For example the type list has an argu-
ment to specify the type of elements occurring in the list. However this
type system is not flexible enough to be used with meta-programming
predicates, such as arg/3,=../2 or assert/1.
Subtyping is a fundamental concept introduced by Cardelli [2] and
Mitchell [14]. The power of subtyping resides in the subtyping rule which
states that an expression of type τcan be used instead of an expression
of type τ0provided that τis a subtype of τ0:
(Sub)U`t:τ , τ τ0
U`t:τ0
Subtyping can be used to deal with meta-programming by the introduc-
tion of a type term as a supertype of all types. For example, the subtype
relation list(α)term, allows to type check the query arg(N,[X|L],T),
using the type int ×term ×term pred for arg/3, although the second
argument is a list. Subtyping can also be used for coercions between
constraint domains. For example, it is possible to share variables be-
tween CLP(B), with type boolean, and CLP(F D), with type int , simply
1
17
by adding the subtyping relation boolean <int. This way Bvariables
can be used with F D predicates.
Most of the type systems with subtyping that where proposed for
constraint logic programs are descriptive type systems, i.e. they aim to
describe the set of terms for which a predicate is true. On the other hand,
there where only few prescriptive type systems with subtyping for logic
programming [1, 7, 13, 16, 18]. Moreover, in these systems, subtyping
relations between type constructors with different arities, as in list(α)<
term, are not allowed. Algorithms to deal with such subtyping relations,
called non-structural non-homogeneous subtyping, can be found in [17,
20] in the case where the subtyping order forms a lattice, or in [4] for
the case of quasi-lattices.
The combined use of subtyping and parametric polymorphism thus
offers a great programming flexibility. Still, it can not address the first
example given in this paper, that is -/2 being viewed sometimes as the
arithmetic operator and sometimes as a constructor of pairs (as in the
predicate keysort/2). The solution to this problem resides in overload-
ing. Overloading consists in assigning multiple types to a single symbol.
This notion has already been used in numerous languages, such as C,
to deal with multiple kinds of numbers in arithmetic operations. With
overloading, -/2 can have both type int expr ×int expr int expr and
type α×βpair(α, β ).
In this paper, we describe TCLP, a type checker for Prolog/CLP(X),
written in SICStus Prolog with Constraint Handling Rules (CHR) [9].
The type system of TCLP combines parametric polymorphism, subtyp-
ing and overloading in order to keep the flexibility of the traditionally
untyped CLP(X) languages, yet statically detecting programming er-
rors. Section 2 shows examples of how the type system takes advantage
of these three features. Section 3 presents the type system of TCLP. In
section 4, we describe the basic type declarations and output of TCLP,
while section 5 shows how the type system can be extended to han-
dle constraint solver programming, like new CLP(FD) constraints or
CHR rules. Some benchmarks are presented in section 6 and section 7
concludes.
2 Motivating examples
The aim of the TCLP type checker is to introduce a typing discipline in
constraint logic programs in order to find programming errors, while of-
fering enough flexibility for practical programming. That means dealing
with Prolog/CLP(X) programming facilities like meta-programming or
the simultaneous use of multiple constraint solvers. This goal is achieved
using a combination of parametric polymorphism, subtyping and over-
loading. In the rest of this section, we give examples of how they are
used in TCLP.
2.1 Prolog examples
A first use of parametric polymorphism is the typing of structures that
may be used with any type of data. For example, using the type list(α)
for lists allows typing [1,2] with the type list(int ) and [’a’,’b’] with
the type list(char ). A consequence is the use of polymorphic types for
predicates manipulating these data structures in a generic way. For
18
example, the type of the predicate append/3 for concatenating lists is
list(α)×list(α)×list (α)pred. Of course, some other predicates may
use non generic types when manipulating the data inside structures, like
sum list/2 having type list (int )×int pred .
Another use of parametric polymorphism is for constraints or pred-
icates that can be used on any term, the best example being =/2 with
type α×αpred . This type simply express that the two arguments
of =/2 must have the same type. Another example resides in term com-
parison predicates like ’@=<’/2, which also has type α×αpred .
On the other hand, predicates for manipulating terms cannot be
typed using only parametric polymorphism. An example is the predi-
cate =../2 for decomposing terms. Indeed T=..L unifies Lwith the list
constituted by the head constructor of Tand the arguments of T. This
means that Lis an non-homogeneous list. Subtyping provides a solution
for typing this predicate, through the introduction of the type term as
the supertype of all types, that is for all types τ,τterm . Using the
type term ×list(term)pred for =../2, it is possible to type check a
query like [1] =.. [’.’,1,[]] with type list (int) for [1],atom for
’.’,int for 1,list(α) for [] and list(term) for [’.’,1,[]].
Subtyping is also interesting when typing programs that use dynamic
predicates, using assert/1. The type of assert/1 is clause pred and
the type of ’:-’/2 is pred ×goal clause . This allows typing queries
like assert((p(X) :- X<1)). However, without subtyping, queries like
assert(p(1)) are not correctly typed because p(1) would be typed
pred , while assert/1 expects the type clause . Using subtyping with
pred <clause ,p(1) is seen with the type clause and the query is well-
typed.
The operator -/2, as showed in the introduction, provides a good
example of the use of overloading, with types int expr ×int expr
int expr,float expr ×int expr float expr,int expr ×float expr
float expr,float expr ×float expr float expr and α×βpair(α, β ).
This example shows the more classical overloading of -/2 with respect
to the different kinds of number as well as its use as a coding for pairs.
In this case, subtyping can also be used to deal with the different kind
of numbers, with int expr <float expr, using the type α×αα, α
float expr. However, in the Prolog dialects that we considered, the
unification 1=1.0 fails. This led us to choose overloading instead of
subtyping for dealing with numerical expressions, thus making a clear
distinction in types between integers and floats. An other example is
=/2. It is used both as the equality constraint and to build pairs of
the form Name=Var in an option of the predicate read term/3. Thus
is has both types α×αpred and atom ×term varname. Other
examples include options shared by several different predicates or ’,’/2
used both as the conjunction and as a constructor for sequences.
2.2 Combining constraint domains
A first example is the combination of the Herbrand domain CLP(H) with
an other domain, such as CLP(FD). Prolog is mainly used to handle
data structures and for posting constraints. However there can be a
stronger interaction when defining, e.g., predicates for labelling. The
type used to represent F D is int , already present in the type hierarchy
of CLP(H). This way FD variables can be also used as Prolog variables
when needed.
19
Another interesting example is combining CLP(FD) and CLP(B).
Indeed, variables can be shared between the two constraint solvers. This
is possible when Bis represented as the set {0,1}. In this case 0and 1
have type boolean and boolean <int. In this way, Bvariables can also
be used with F D constraints.
A last example is reified constraints. This represent a combination
of CLP(H), CLP(FD) and CLP(B). Constraints like ’#<=>’/2 accept
other constraints as arguments. In order to handle these cases, F D
constraints are typed with type fd constraint. The subtype relations
fd constraint <pred and fd constraint <boolean expr allows these
constraints to be used both in boolean expressions and as predicates
in Prolog clauses.
3 The type system
3.1 CLP(X) programs
CLP programs are built upon a denumerable set Vof variables, a finite
set Sof symbols, given with their arity, a set F ⊆ S of function symbols
and a set P ⊆ S of predicate and constraint symbols. Pis supposed to
contain the equality constraint symbol =/2. Terms are built upon F ∪V.
An atom is of the form p(t1,...,tn), where p/n ∈ P and t1, . . . , tnare
terms. A query is a finite sequence of atoms. When it is necessary to
distinguish predicate atoms (built using a predicate symbol) and con-
straint atoms (built with a constraint symbol), queries are noted c|α
where cis the constraint part of the query and αis the predicate part
of the query. A clause is an expression AQwhere Ais a predicate
atom and Qis a query. A constraint logic program is a set of clauses
and queries.
The execution model we consider for constraint logic programs is the
CSLD rewriting relation :
Definition 1 Let Pbe a CLP(X) program. The rewriting relation
CSLD over queries is defined as the smallest relation satisfying the
following CSLD rule:
p(N1,...,Nk)c0|A1,...,Anθ(P)
X |=(cM1=N1...Mk=Nkc0)
c|α, p(M1,...,Mk), α0CSLD
c, M1=N1,...,Mk=Nk, c0|α, A1,...,An, α0
where θis a renaming of the clause with fresh variables.
3.2 Types
Types are (possibly infinite) terms built upon a signature of type con-
structors, denoted by κ, and type variables also called parameters, noted
α, β, . . .. Types are noted τand the set of types is noted T. The subtyp-
ing order on types is induced by an order <Kon type constructors and
a relation ικ12between the argument positions of each pair (κ1, κ2) of
type constructors. For all type constructors κ1, κ2,ικ12is an injec-
tive partial function and ι1
κ12=ικ21. For all κ1Kκ2Kκ3,ικ13=
ικ23ικ12. For two types τ=κ(τ1,...,τm) and τ0=κ0(τ0
1, . . . , τ 0
n),
ττ0if and only if κKκ0and for all i, j ικ,κ0,τiτ0
j. Moreover
the type order is supposed to form a quasi-lattice, that is a partial or-
der where the existence of a lower (resp. upper) bound to a non-empty
20
set of types implies the existence of a greatest lower bound (resp. least
upper bound) for this set. A type substitution is a mapping from type
variable to types, extended the usual way into a mapping from types to
types. A type substitution Θ is ground if for all type variable α, Θ(α)
is ground.
Ground types are interpreted as sets of terms, while non ground types
are interpreted as mappings from ground substitutions to sets of terms.
For example, the type list (int ) is interpreted as the set of the lists of
integers, while the infinite type list (list(...)) is interpreted as the set of
lists that contain only lists that contain only lists ... 1. The subtyping
relation is interpreted as the inclusion of these sets of terms. A more
formal description of types and of the subtyping relation can be found
in [4].
To each functor f/n is associated a set types(f /n) of type schemes
of the form α1...αnτ1×...×τnτ, (abbreviated τ1×...×τn
τ), where {α1,...,αn}is the set of variables appearing in τ1×. . . ×
τnτ. We assume the existence of a particular type pred for the type
of predicates: for all predicate and constraint symbols p/n ∈ P, it is
supposed that there is at least one type scheme τ1×...×τ2τ
types(p/n) such that τpred. On can note that some symbols may
be overloaded both as function symbols and predicates symbols, such as
=/2 with types α.α ×αpred and atom ×term varname .
3.3 Well typed programs
The typing rules of TCLP, given in Table 1, allow to deduce type judg-
ment of the form U`typed expression, where Uis a typing environment,
that is a mapping from Vto T. A clause p(t1,...,tn)Qis well-typed
if for all type schemes τ1×...×τnτtypes(p/n) with τpred,
there exists a typing environment Usuch that U`p(t1, . . . , tn)
QClauseτ1×...×τn. A program is well-typed if all its clauses are well-
typed. A query Qis well-typed if there exists a typing environment U
such that U`QQuery.
Basically, the type system of TCLP adds the subtyping rule [2, 14]
to the rules of Mycroft and O’Keefe [15]. Overloading is handled in the
side condition of rules (Func), (Atom) and (Head ) by considering all
possible type schemes for each occurrence of overloaded symbols. The
type annotations appearing in the rules (Head) and (Clause ) are used to
keep track of the type used for the head of the clause. The distinctions
between rules (Head) and (Atom) express the principle of definitional
genericity [12], that the type of the head of a clause must be equivalent
up-to renaming to the type of the predicate defined by this clause. This
condition of definitional genericity is useful for the correctness properties
(“subject reduction”) of the type system [3, 8]. The rule (Head), used
for typing heads of clauses, thus allows only renaming substitutions of
the type declared for the predicate.
Theorem 1 (subject reduction) [3] Let Pbe a well-typed program
and Qa well typed query, i.e. U`QQuery for some typing environment
U. If QCSLD Q0then there is a typing environment U0such that
U0`Q0Query.
1this does not mean that the terms in this set are infinite: for example [],[[]] and [[],[]]
are in this set.
21
(Var){x:τ,...} ` x:τ
(Func)U`t1:σ1σ1τ1Θ... U`tn:σnσnτnΘ
U`f(t1,...,tn):τΘ
where Θ is a type substitution
and τ1×...×τnτtypes(f/n)
(Atom)U`t1:σ1σ1τ1Θ... U `tn:σnσnτnΘ
U`p(t1,...,tn)Atom
where Θ is a type substitution
and τ1×...×τnτtypes(p/n), with τpred .
(Head)U`t1:σ1σ1τ1Θ... U `tn:σnσnτnΘ
U`p(t1,...,tn)Headτ1×...×τn
where Θ is a renaming substitution
and τ1×...×τnτtypes(p/n), with τpred .
(Query)U`A1Atom ... U`AnAtom
U`A1,...,AnQuery
(Clause)U`QQuery U`AHead τ1×...×τn
U`AQClauseτ1×...×τn
Table 1: The TCLP typing rules with overloading.
It is worth noting that the CSLD resolution is an abstract execution
model, which proceeds only by constraint accumulation. The theorem
above does not hold for more concrete execution models that perform
substitution steps. Let us consider the predicates p/1 and q/1, with
int pred types(p/1) and byte pred types(q/1). Let us suppose
that p/1 is defined by p(500). The query p(X),q(X) is well typed with
X:byte. A step of CSLD resolution produces the query X=500,q(X).
A substitution step produces the query p(500), which is not well typed
since 500 does not have type byte. This can be viewed as a weakness
of the type system, but we believe this is the price to pay for flexibility.
Moreover, it is possible to keep the type of variables at run-time in order
to get stronger subject reduction theorem [8] for an execution model that
performs substitution steps.
3.4 Type checking
The typing rules of Table 1 are syntax directed. Without overloading,
the type checking algorithm, given a typing environment Uand the type
of symbols, basically collects subtype inequalities along the derivation
of the expression to check and then check the satisfiability of collected
subtyping constraints, using the algorithm described in [4]. This type
checking algorithm can be extended to infer a typing environment U
for which the expression is well-typed, simply by replacing the type of
variables appearing in the expression to type check by parameters. Then
checking the satisfiability of the resulting subtyping constraint system
determines the existence of a typing environment U.
22
Overloading introduces non-determinism in the rules (Func) and
(Atom). For type checking expressions, subtype inequalities are first
collected along the derivation by replacing the type of overloaded sym-
bols by type variables. Then the possible typings for each occurrence of
overloaded symbols are enumerated by checking the satisfiability of the
subtype constraint system. In order to remain efficient, the enumeration
proceeds with the Andorra principle. This principle, first introduced for
the parallelization of Prolog [5], consists in delaying choice points until
time where all deterministic goals have been executed. This strategy
proves to be sufficient to deal with overloading in TCLP, mainly be-
cause in most cases the type information coming from the context of an
expression is sufficient to disambiguate the type of overloaded symbols
in this expression.
The type checking algorithm used in TCLP is simply the combination
of the type inference for variables with the enumeration of possible types
for overloaded symbols.
3.5 Type inference for predicates
In a prescriptive type system, type reconstruction can be used to omit
type declarations and still type check the program by inferring the type
of undeclared predicates using their defining clauses [12], if it exists,
and raising an error otherwise. Since in TCLP, a predicate can accept
any argument of a subtype of the type of declared predicate, the type
term ×...×term pred is always a possible type. Because this type
is not very informative, we use a heuristic type inference algorithm [8].
Basically it tries to combine the different type informations taken from
the functors and variables appearing the head of the defining clauses to
deduce a more informative type. In the presence of overloaded symbols,
several heuristic types can be found by enumerating the possible types
for these symbols. The current implementation uses only the first one in
the typing of the remaining part of the program. This choice was made
to avoid the multiplication of overloaded predicates. The enumeration
proceeds by first choosing the last declared type for each overloaded
symbol. This enumeration strategy proves to be right most of the time,
because the last declared type for an overloaded symbol usually corre-
spond to the currently defined predicate.
4 Standard use of TCLP
4.1 Type declarations
We now introduce the concrete syntax of TCLP type declarations. These
declarations take the form of Prolog directives. They can be placed
either in the program source or in a separated file with the suffix .typ.
They consist in type constructor declarations, type order declarations
and type scheme declarations.
Type constructor declarations are done using any one of the following
two syntaxes:
:- type t/n. :- type t(A1,...,An).
Both directives declare a a type constructor twith narguments. For
example the type constructor list can be declared by
23
:- type list/1.
Type order declarations are done using the directive order:
:- order t(A1,...,Am)<u(B1,...,Bn).
which declares that t<Ku. The relation ιt,u is deduced from the variables
appearing as arguments: if Ai=Bjthen (i, j)ιt,u . For example:
:- order assoc(A,B) < tree(B).
declares that assoc<Ktree and that ιassoc,tree ={(2,1)}.
The syntax for declaring type schemes is:
:- typeof f(t1,...,tn) is t.
where tiand tare types. This declares that the type scheme t1×...×
tntis in types(f /n). For example:
:- typeof append(list(A),list(A),list(A)) is pred.
declares that α.list(α)×list (α)×list (α)pred types(append/3).
Overloaded symbols simply have several declarations (one per type
scheme).
Type constructor and type scheme syntax can also be combined:
:- type list(A) is [ [] , [ A | list(A) ] ].
is syntactic sugar for
:- type list/1.
:- typeof [] is list(A).
:- typeof [ A | list(A) ] is list(A).
In addition to explicit declarations, TCLP implicitly adds default
declarations. For every declared type constructor κ, the declaration that
κ <Kterm is added to ensure that it is still a supertype of all types.
Numbers are implicitly declared to have either type byte,int or float. All
non-numeric constants are declared to have type atom except for char-
acters, which are declared to have type char with char <Katom. Still,
thanks to overloading, non-numeric constants may also have other types
corresponding to their use in specific situations. For example, write/0
has both the type atom and the type io mode. Using these types, the
following query, for opening a file named “write” in writing mode, is
well typed: open(write,write,Stream), the first occurrence of write
being typed as atom and the second as io mode. Finally any func-
tor f/n that has no declared type scheme has the default type scheme
term ×...×term term.
4.2 TCLP invocation
TCLP can be used either as a stand-alone executable (by typing tclp
file.pl in the shell) or as a library for SICStus Prolog. When invoked,
TCLP determines and loads a standard type library, usually named
stdlib.typ. This library contains the type definitions and types for
built-in predicates of the selected Prolog dialect, currently either ISO,
GNU or SICStus Prolog. In the case of SICStus Prolog, type files for
each library are automatically loaded when encountering the correspond-
ing use module directive.
24
When invoked on a source file, TCLP prints the types inferred for
undeclared predicates using the syntax for type scheme declarations.
This allows to reuse the types inferred by TCLP for type checking other
libraries or same file after some modifications. For example, the type
inference of the predicate append/3:
append([],L,L).
append([X|L],L2,[X|R]) :- append(L,L2,R).
produces the following output:
:- typeof append(list(A),list(A),list(A)) is pred.
If a type error is encountered, TCLP prints it and exits immediately.
Here we give examples of ill-typed queries and clauses with the error
message displayed by TCLP:
Illegal type for an argument
:- X is Y << 3.5 .
! Incompatible type : 3.5 has type float but is
required to have type int_expr
No type can be found for a variable
:- length(N,L), member(a,L).
! Incompatible types for L : int and list(top)
Violation of definitional genericity
:- typeof p(list(A)) is pred.
p([1]).
! Incompatible type : 1 has type byte but is
required to have type A
Error on an overloaded symbol
:- X is 3 << (2 - 3.5).
! Can’t find a good type for (-)/2
5 Advanced definitions
An interesting feature of TCLP is the possibility to extend the typing
rules. The aim is the type checking of phrases that are similar to clauses
from the type checking point of view. This extension uses declarations
that specify how these phrases must be cut into sets of heads and bod-
ies. The heads are type checked using rules similar to the (Head) rule
and the bodies are type checked as queries. Note, however, that a new
subject reduction theorem must be proved in order to ensure the cor-
rectness of the system thus obtained. We show two examples of type
system extensions, one for primitive CLP(FD) constraints definitions in
SICStus Prolog and another for the CHR language [9].
25
5.1 CLP(F D) primitive constraints
In SICStus, primitive constraints can be declared using ’+:’/2,’-:’/2,
’+?’/2 and ’-?’/2. In order to type check these declarations one may
want to introduce new typing rules. This is achieved using the declara-
tion
:- tclp__define_clause_op(BinOp,Type).
where BinOp is the binary operator that separates the head and the
body and Type is the type of the head. For example, the declaration
:- tclp__define_clause_op(’+:’,fd_constraint).
adds the following typing rule:
U`HHead0U`BQuery
U`H+: B Clause
where U`HHead0is derived using the rule (Head 0), which differ from
(Head) only by the side condition: τpred becomes
τfd constraint in (Head 0).
5.2 CHR rules
There are three kinds of CHR rule: C==> Q(propagation rule),
C<=> Q(simplification rule) and C1\C2<=> Q(simpagation rule,
i.e. both a simplification rule and a propagation rule). C,C1and C2
are sequences of CHR constraints. Qis either a query or Q1|Q2where
Q1and Q2are queries. In order to handle these rules, the declaration
tclp define clause/5 is used. We refer to the TCLP documenta-
tion for the precise syntax of these declarations. The declarations for
CHR rules are given in appendix A. Here we give the typing rule for
propagation rules (other rules are similar). Type judgment of the form
U`HHead00 are derived from a rule (Head 00 ) similar to (Head) excepted
that the side condition τpred is replaced by τchr constraint.
U`H1Head00 . . . U `HnHead00 U`BQuery
U`H1,...,Hn<=> BClause
Type inference can still be used with the new type system, as shown
in the following example. This example consists in a constraint solver
for finding greatest common divisor and was taken from the CHR web
page. The CHR rules:
gcd(0) <=> true.
gcd(N) \ gcd(M) <=> N=<M | L is M mod N, gcd(L).
produce the following output in TCLP
:- typeof gcd(int) is chr_constraint.
6 Experimental evaluation
The performance of the system has been evaluated on a GNU/Linux 2.4
system with an Intel Pentium 4 CPU at 2 GHz, 256 Mb of RAM using
SICStus 3.9.1 and a preliminary version of TCLP 0.4. Running times
for 16 SICStus Prolog libraries are shown in Table 2. The first column
indicates the name of the library. The remaining column are divided in
two groups: the first group indicates running times when using pure type
26
checking, that is without type inference for predicates, while the second
group indicates running times using type inference for all predicates that
are not exported by the library. Each group contains three columns. The
first one, Overld, is the time consumed to solve ambiguous overloaded
symbols. The second one, T.check indicates the type checking time
(including type inference in the case of the second group). The last
column, Total, indicates the running total time, including loading type
libraries and building the resulting type order.
Pure type checking With predicate type inference
File Overld T.check Total Overld T.check Total
arrays 0.18 s 0.80 s 2.52 s 0.19 s 1.00 s 2.73 s
assoc 0.52 s 2.16 s 3.89 s 0.87 s 3.88 s 5.61 s
atts 0.75 s 1.92 s 3.72 s 1.35 s 3.26 s 5.04 s
bdb 0.84 s 3.14 s 6.08 s 1.07 s 4.19 s 7.06 s
charsio 0.07 s 0.40 s 1.99 s 0.09 s 0.43 s 2.00 s
clpr 29.10 s 47.05 s 49.68 s 97.60 s 142.49 s 145.76 s
fastrw 0.05 s 0.20 s 1.83 s 0.12 s 0.32 s 1.98 s
heaps 0.49 s 1.87 s 3.58 s 1.51 s 5.50 s 7.24 s
jasper 0.32 s 0.98 s 3.21 s 0.48 s 1.36 s 3.52 s
lists 0.96 s 1.86 s 3.44 s 1.23 s 2.63 s 4.24 s
ordsets 0.89 s 2.35 s 3.92 s 3.64 s 7.33 s 8.92 s
queues 0.12 s 0.44 s 2.14 s 0.17 s 0.55 s 2.26 s
sockets 0.82 s 1.83 s 4.02 s 0.77 s 2.12 s 4.15 s
terms 0.44 s 1.32 s 2.90 s 0.54 s 1.72 s 3.31 s
trees 0.27 s 0.79 s 2.47 s 0.32 s 1.17 s 2.89 s
ugraphs 7.39 s 14.17 s 16.28 s 11.20 s 31.97 s 34.04 s
Table 2: Running times
Running times prove that TCLP is fast enough to be used in practice,
the worst time being obtained for the clpr library which represents about
4400 lines of code and 527 inferred predicates. When running on small
files, most of the running time is used to compute all data structures
related to TCLP declarations. These computations usually take 2 to
3 s depending on declarations that are specific to each library. The
time used to solve overloaded symbols is very low, usually less than 50%
(68% in the worst case) of the total type checking time, thanks to the
enumeration strategy. The overhead of type inference w.r.t. pure type
checking can be explained by the fact that pure type checking considers
the program clauses one by one, while type checking with predicate type
inference considers clauses grouped by strongly connected components of
the call graphs, which leads to considerably larger subtyping constraint
systems and to a higher number of overloaded symbols to be treated at
once.
7 Conclusion
We presented TCLP, a prescriptive type checker for Prolog/CLP(X),
which can be used with practical constraint logic programs. Thanks to
parametric polymorphism, subtyping and overloading, it can type check
queries and goals using generic data structures, term decomposition and
meta-programming predicates, overloaded symbols such as ’-’/2, or the
combination of multiple constraint solvers including reified constraints.
The possibility to extend the type system, makes it possible to use TCLP
27
for constraint solver programming like extending CLP(FD) with new
constraints or using the CHR language. TCLP features type inference
for variables and for predicates, so the user can get rid of numerous
type declarations. The experimental evaluation of TCLP on 16 SICStus
Prolog libraries, including CLP(R), proved that the type checker is fast
enough to be used in practice. For these reasons, we believe that TCLP
is a good tool for type checking constraint logic programs.
As future work, we intend to develop a formalization of the extensions
of the type system. We also want to extend TCLP to other Prolog
dialects such as, e.g., Ciao Prolog or SWI Prolog.
Availability TCLP is distributed under the GNU Lesser General Pub-
lic License, and is available as sources, binaries for Linux/x86 and
MacOSX or as a library for SICStus Prolog. An online demo can be
found on the TCLP web site:
http://contraintes.inria.fr/~coquery/tclp
References
[1] C. Beierle. Type inferencing for polymorphic order-sorted logic
programs. In 12th International Conference on Logic Programming
ICLP’95, pages 765–779. The MIT Press, 1995.
[2] L. Cardelli. A semantics of multiple inheritance. Information and
Computation, 76:138–164, 1988.
[3] E. Coquery and F. Fages. TCLP: overloading, subtyping and para-
metric polymorphism made practical for constraint logic program-
ming. Technical report, INRIA Rocquencourt, 2002.
[4] E. Coquery and F. Fages. Subtyping constraints in quasi-lattices.
In P. Pandya and J. Radhakrishnan, editors, Proceeding of the 23rd
Conference On Foundations Of Software Technology And Theoret-
ical Computer Science, LNCS. Springer, 2003.
[5] V. Santos Costa, D.H.D. Warren, and R. Yang. The Andorra-I pre-
processor: Supporting full Prolog on the basic Andorra model. In
Proceedings of the 8th International Conference on Logic Program-
ming ICLP’91, pages 443–456. MIT Press, 1991.
[6] Luis Damas and Robin Milner. Principal type-schemes for func-
tional programs. In ACM Symposium on Principles of Program-
ming Languages (POPL), pages 207–212, 1982.
[7] R. Dietrich and F. Hagl. A polymorphic type system with sub-
types for Prolog. In H. Ganzinger, editor, Proceedings of the Euro-
pean Symposium on Programming ESOP’88, LNCS, pages 79–93.
Springer-Verlag, 1988.
[8] F. Fages and E. Coquery. Typing constraint logic programs. Theory
and Practice of Logic Programming, 1(6):751–777, November 2001.
[9] T. Fr¨uhwirth. Theory and practice of Constraint Handling Rules.
Journal of Logic Programming, Special Issue on Constraint Logic
Programming, 37(1-3):95–138, October 1998.
[10] P. Hill and J. Lloyd. The G¨odel programming language. MIT Press,
1994.
28
[11] J. Jaffar and J.L. Lassez. Constraint logic programming. In Pro-
ceedings of the 1987 Symposium on Principles of Programming Lan-
guages POPL’87, pages 111–119, 1987.
[12] T.K. Lakshman and U.S. Reddy. Typed Prolog: A semantic recon-
struction of the Mycroft-O’Keefe type system. In V. Saraswat and
K. Ueda, editors, Proceedings of the 1991 International Symposium
on Logic Programming, pages 202–217. MIT Press, 1991.
[13] G. Meyer. Type checking and type inferencing for logic programs
with subtypes and parametric polymorphism. Technical report,
Informatik Berichte 200, Fern Universitat Hagen, 1996.
[14] J. Mitchell. Coercion and type inference. In Proceedings of the 11th
Annual ACM Symposium on Principles of Programming Languages
POPL’84, pages 175–185, 1984.
[15] A. Mycroft and R.A. O’Keefe. A polymorphic type system for
Prolog. Artificial Intelligence, 23:295–307, 1984.
[16] F. Pfenning, editor. Types in Logic Programming. MIT Press, 1992.
[17] F. Pottier. Simplifying subtyping constraints: a theory. To appear
in Information and Computation, 2002.
[18] G. Smolka. Logic programming with polymorphically order-sorted
types. In Algebraic and Logic Programming ALP’88, number 343 in
LNCS, pages 53–70. J. Grabowski, P. Lescanne, W. Wechler, 1988.
[19] Z. Somogyi, F. Henderson, and T. Conway. The execution algo-
rithm of Mercury, an efficient purely declarative logic programming
language. Journal of Logic Programming, 29(1–3):17–64, 1996.
[20] V. Trifonov and S. Smith. Subtyping constrained types. In Proceed-
ings of the 3rd International Static Analysis Symposium SAS’96,
number 1145 in LNCS, pages 349–365, 1996.
A TCLP declarations for CHR rules
We use an auxiliary Prolog file, chrcore.pl, to decompose CHR rules
in sets of heads and bodies. The predicate chr heads/3 decomposes a
sequence of heads into a list of Head-Location-Type triplets, while the
predicate chr clauses/4 breaks a rule into a body and a list of heads.
In the last clause, the type chr constraint is specified, which leads TCLP
to use the rule (Head00).
The predicate user:arg location/2 is predefined in TCLP and is
used to provide the location of the different parts of the rule in the pro-
gram source code to TCLP, mainly for reporting errors in the right place.
myappend([],X,X).
myappend([X|L],L2,[X|R]) :- myappend(L,L2,R).
%% rule decomposition
chr__clause((HeadsDef <=> Body), Location,
Heads, [ Body - BodyLoc ]) :-
user:args_location(Location,[HeadsLoc, BodyLoc]),
chr__heads(HeadsDef, HeadsLoc, Heads).
chr__clause((HeadsDef ==> Body), Location,
Heads, [ Body - BodyLoc ]) :-
29
user:args_location(Location,[HeadsLoc, BodyLoc]),
chr__heads2(HeadsDef, HeadsLoc, Heads).
%% sequence of heads to list
chr__heads((H1\H2), Location, Heads) :- !,
user:args_location(Location,[L1,L2]),
chr__heads2(H1,L1,Heads1),
chr__heads2(H2,L2,Heads2),
myappend(Heads1, Heads2, Heads).
chr__heads(H,L,Hds) :-
chr__heads2(H,L,Hds).
chr__heads2((H1,H2), Location, Heads) :- !,
user:args_location(Location,[L1,L2]),
chr__heads2(H1,L1,Heads1),
chr__heads2(H2,L2,Heads2),
myappend(Heads1, Heads2, Heads).
chr__heads2(H,L,[H-L-chr_constraint]).
The following code comes from the type declaration file for the CHR
library, chr.typ. The first directive loads the code from chrcore.pl.
The two last directives define, given a rule and its location, a list of heads
and a list of bodies, using chr clause/4 from chrcore.pl. Using these
directives, TCLP will decompose CHR rules in sets of heads and bodies,
heads being type checked with the rule (Head 00), while bodies are type
checked as queries. The difference between the second and the third
directive is that the second directive discards the name of rules (names
are given to rules in CHR using the notation N ame @Rule).
%% load prolog code for parsing CHR rules
:- tclp__load_prolog(tclplib(’sicstus/chrcore.pl’)).
%% the declarations simply consist in the call to predicates
%% defined in chrcore.pl
:- tclp__define_clause((_ @ Rule), Location, Heads, Bodies,
(user:args_location(Location,
[_,RuleLoc]),
chr__clause(Rule, RuleLoc,
Heads, Bodies))).
:- tclp__define_clause(Rule, Location, Heads, Bodies,
chr__clause(Rule, Location,
Heads, Bodies)).
30
Analyzing and Visualizing PRO L OG Programs
based on XM L Representations
Dietmar Seipel1, Marbod Hopfner2,
Bernd Heumesser2
1University of Würzburg, Institute for Computer Science
Am Hubland, D – 97074 Würzburg, Germany
seipel@informatik.uni-wuerzburg.de
2University of Tübingen, Wilhelm–Schickard Institute for Computer Science
Sand 13, D – 72076 Tübingen, Germany
{hopfner, heumesser}@informatik.uni-tuebingen.de
Abstract
We have developed a PROL O G package VI SU R/RAR for reasoning about various types of
source code, such as PROL O G rules, JAVA programs, and XSLT stylesheets. RAR provides
techniques for analyzing and improving the design of PROL OG programs, and it allows for
implementing software engineering metrics and refactoring techniques based on XM L repre-
sentations of the investigated code. The obtained results are visualized by graphs and tables
using the component VI SU R.
VIS UR /R A R can significantly improve the development cycle of logic programming ap-
plications, and it facilitates the implementation of techniques for syntactically analyzing and
visualizing source code. In this paper we have investigated the dependency structure between
the different rules and the hierarchical structure of PROL O G software systems, as well as the
internal structure of individual predicate definitions.
For obtaining efficiency and for representing complex deduction tasks we have used tech-
niques from deductive database and non–monotonic reasoning.
Keywords. comprehension, refactoring, reasoning, visualization, PRO LO G, XML
1 Introduction
For many programming languages, powerful integrated development environments (IDEs) have been
developed, such as IBM’s Eclipse for JAVA [12], and Together for JAVA, C++, Visual Basic, etc.
They contain tools such as editors with syntax highlighting,tracing and debugging and tools for
graphical programming. Advanced IDEs support programmers in managing large projects, e.g. by
facilitating the tasks of correcting, completing and reusing source code. In the logic programming
community, so far only few tools exist for comfortably programming and for analyzing source code,
cf., e.g., the IDEs for XPC E–PRO LO G [20] and for Visual PR OL OG , and the tool Cider [7] for the
functional–logic language Curry.
31
The package VIS U R/RA R [9] provides some essential functionality of an IDE for PRO L OG. It
allows for the visualization of rules (VI S UR: Visualization of Rules) together with the inference over
rule structures (RAR : Reasoning about Rules). VI SU R/RAR is a part of the toolkit DIS LOG , which
is developed under XPC E/SWI –PROL OG [20]. The functionality of DIS LOG ranges from (non–
monotonic) reasoning in disjunctive deductive databases to applications such as the management
and visualization of stock information.
The goal of the system VIS UR /RAR is to support the application of software engineering and
refactoring techniques, and the further system development. VI SU R/RA R facilitates program com-
prehension and review, design improvement by refactoring, the extraction of subsystems, and the
computation of software metrics (such as, e.g., the degree of abstraction). It helps programmers in
becoming acquainted with source code by visualizing dependencies between different predicates or
source files of a project. It is possible to analyse source code customized to the individual needs of
a user, and to visualize the results graphically or in tables.
VIS UR /RA R can be applied to various kinds of rule–based systems, including expert systems,
diagnostic systems, XS LT stylesheets, etc. In this paper we have applied VI SU R/RAR to the source
code of the system DIS LOG , which currently contains about 80.000 lines of code in SW I–PROL O G
in about 10.000 PRO LO G rules. In previous papers [9, 10] we have shown how also JAVA source
code can be analysed using VIS UR /RA R. For gaining sufficient performance on large programs such
as DI SLO G we use techniques from the field of deductive databases.
The rest of the paper is organized as follows: In Section 2 we introduce our PROL OG library for
managing XM L documents. In Section 3 we briefly describe the graph visualization tool VI SU R. In
Sections 4 and 5 we investigate some typical problems and questions that might be asked about the
global and the local structure of PR OL OG rules, respectively, and we show how we can solve these
problems using RA R.
2 Representation of Source Code
In VI SU R/RA R complex structured objects, such as JAVA programs, PROL OG programs, and XSLT
stylesheets, are conceptually treated in two different XML notations; for handling these XML data
we use the library FNQU ERY, which is part of the DI S LOG unit xml [17].
Firstly, RAR uses a notation which is similar to RuleML [19] for representing PRO LO G defin-
tions and rules; our DTD differs slightly from RuleML, and moreover we only use some of the
elements and attributes mentioned in RuleML. Secondly, VIS U R transforms our XM L representa-
tion of rules and our reportings into the Graph eXchange Language GXL [11]; we added some
additional attributes to the GX L notation for configuring the graph display.
2.1 PROL OG Programs in XM L
The following PR OL OG predicate tc/2 computes the transitive closure of the relation arc/2:
tc(U1, U2) :-
arc(U1, U3), tc(U3, U2).
tc(U1, U2) :-
arc(U1, U2).
32