Writing and running applicationsï
For a successful programming in Logtalk, you need a good working knowledge of Prolog and an understanding of the principles of object-oriented programming. Most guidelines for writing good Prolog code apply as well to Logtalk programming. To those guidelines, you should add the basics of good object-oriented design.
One of the advantages of a system like Logtalk is that it enable us to use the currently available object-oriented methodologies, tools, and metrics [Champaux92] in logic programming. That said, writing applications in Logtalk is similar to writing applications in Prolog: we define new predicates describing what is true about our domain objects, about our problem solution. We encapsulate our predicate directives and definitions inside new objects, categories, and protocols that we create by hand with a text editor or by using the Logtalk built-in predicates. Some of the information collected during the analysis and design phases can be integrated in the objects, categories and protocols that we define by using the available entity and predicate documenting directives.
Starting Logtalkï
We run Logtalk inside a normal Prolog session, after loading the necessary files. Logtalk extends but does not modify your Prolog compiler. We can freely mix Prolog queries with the sending of messages and our applications can be made of both normal Prolog clauses and object definitions.
Depending on your Logtalk installation, you may use a script or a shortcut to start Logtalk with your chosen Prolog compiler. On POSIX operating-systems, Bash shell integration scripts should be available from the command-line. On Windows, PowerShell integration scripts should be available from the command-line and integration shortcuts should be available from the Start Menu. Scripts are named upon the used backend Prolog compilers.
For example, assuming a POSIX operating-system and GNU Prolog as the backend:
$ gplgt
...
Depending on your Logtalk installation, you may need to type instead
gplgt.sh
. On Windows, using PowerShell 7.2 or later version and
ECLiPSe as the backend:
PS> eclipselgt.ps1
...
Running parallel Logtalk processesï
Running parallel Logtalk processes is enabled by setting the
clean flag to on
. This is the default flag value
in the backend adapter files. With this setting, the intermediate Prolog
files generated by the Logtalk compiler include the processes identifier
in the names, thus preventing file names clashes when running parallel
processes. When the flag is turned off, the generated intermediate Prolog
file names donât include the process identifier and are kept between runs.
This is usually done to avoid repeated recompilation of stable code when
developing large applications or when running multiple test sets for
performance (by avoiding repeated recompilation of the
lgtunit tool).
To run parallel Logtalk processes with the clean
flag turned off, each
process must use its own scratch directory. This is accomplished
by defining the scratch_directory
library alias to a per process
location before loading the compiler/runtime. For example, assuming
weâre using GNU Prolog as the backend, a possible definition could be:
:- multifile(logtalk_library_path/2).
:- dynamic(logtalk_library_path/2).
logtalk_library_path(scratch_directory, Directory) :-
temporary_name(lgtXXXXXX, Name),
decompose_file_name(Name, _, Prefix, _),
atom_concat('/tmp/', Prefix, Directory),
( file_exists(Directory) ->
true
; make_directory(Directory)
).
Assuming the code above is saved in a parallel_logtalk_processes_setup.pl
file, we would then start Logtalk using:
$ gplgt --init-goal "consult('parallel_logtalk_processes_setup.pl')"
The details on how to define and load the definition of the scratch_directory
library alias are, however, backend specific (due to the lack of Prolog
standardization) and possibly also operating-system specific (different
locations for the temporary directory). The Logtalk library includes a
parallel_logtalk_processes_setup.pl
file with support for selected
backends and usage instructions.
Source filesï
Logtalk source files may define any number of entities (objects,
categories, or protocols). Source files may also contain Prolog code
interleaved with Logtalk entity definitions. Plain Prolog code is usually
copied as-is to the corresponding Prolog output file (except, of course,
if subject to the term-expansion mechanism).
Prolog modules are compiled as objects. The following Prolog directives are
processed when read (thus affecting the compilation of the source code that
follows): ensure_loaded/1
, use_module/1-2
, op/3
, and
set_prolog_flag/2
. The initialization/1 directive may
be used for defining an initialization goal to be executed when loading a
source file.
Logtalk source files can include the text of other files by using the
include/1 directive. Although there is also a standard
Prolog include/1
directive, any occurrences of this directive in a
Logtalk source file is handled by the Logtalk compiler,
not by the backend Prolog compiler, to improve portability.
When writing a Logtalk source file the following advice applies:
When practical and when performance is critical, define each entity on its own source file.
Source file loading order can impact performance (e.g. if an object imports a category defined in a source file loaded after the object source file, no static binding optimizations will be possible).
Initialization directives that result in the compilation and loading of other source files (e.g. libraries) should preferably be written in the application loader file to ensure the availability of the entities they define when compiling the application source files (thus enabling static binding optimizations).
Naming conventionsï
When defining each entity in its own source file, it is recommended that
the source file be named after the entity identifier. For parametric objects,
the identifier arity can be appended to the identifier functor. By default,
all Logtalk source files use the extension .lgt
but this is optional
and can be set in the adapter files. For example, we may define an object
named vehicle
and save it in a vehicle.lgt
source file. A sort(_)
parametric object would be saved it on a sort_1.lgt
source file.
Source file text encodingï
The text encoding used in a source file may be declared using the encoding/1 directive when running Logtalk with backend Prolog compilers that support multiple encodings (check the encoding_directive flag in the adapter file of your Prolog compiler).
Multi-pass compilerï
Logtalk is implemented using a multi-pass compiler. In comparison, some Prolog systems use a multi-pass compiler while others use a single-pass compiler. While there are pros and cons with each solution, the most relevant consequence in this context is for the content of source files. In Logtalk, entities and predicates only become available (for the runtime system) after the source file is successfully compiled and loaded. This may prevent some compiler optimizations, notably static binding, if some of the referred entities are defined in the same source file. On the other hand, the order of predicate directives and predicate definitions is irrelevant. In contrast, in a system implemented using a single-pass compiler, the order of the source file terms can and often is significant for proper and successful compilation. In these systems, predicates may become available for calling as soon as they are compiled even if the remaining of the source file is yet to be compiled.
The Logtalk compiler reads source files using the Prolog standard read_term/3
predicate. This ensures compatibility with any syntax extensions that the
used backend may implement. In the first compiler stage, all source file
terms are read and data about all defined entities, directives, predicates,
and grammar rules is collected. Any defined term-expansion rules
are applied to the read terms. Grammar rules are expanded into predicate
clauses unless expanded by user-defined term-expansion rules. The second
stage compiles all initialization goals and clause bodies, taking advantage
of the data collected in the first stage, and applying any defined
goal-expansion rules. Depending on the compilation mode, the generated
code can be instrumented for debugging tools or optimized for performance.
Linter checks are performed during these two first stages. The final step
in the second stage is to write the generated intermediate Prolog code
into a temporary file. In the third and final stage, this intermediate
Prolog file is compiled and loaded by the used backend. These intermediate
files are deleted by default after loading (see the clean
flag description for details).
Compiling and loading your applicationsï
Your applications will be made of source files containing your objects, protocols, and categories. The source files can be compiled to disk by calling the logtalk_compile/1 built-in predicate:
| ?- logtalk_compile([source_file1, source_file2, ...]).
This predicate runs the compiler on each file and, if no fatal errors are found, outputs Prolog source files that can then be consulted or compiled in the usual way by your Prolog compiler.
To compile to disk and also load into memory the source files we can use the logtalk_load/1 built-in predicate:
| ?- logtalk_load([source_file1, source_file2, ...]).
This predicate works in the same way of the predicate
logtalk_compile/1
but also loads the compiled files into memory.
Both predicates expect a source file name or a list of source file names
as an argument. The Logtalk source file name extension, as defined in
the adapter file (by default, .lgt
), can be omitted.
If you have more than a few source files then you may want to use a
loader file helper file containing the calls to the logtalk_load/1-2
predicates. Consulting or compiling the loader file will then compile
and load all your Logtalk entities into memory (see below for details).
With most backend Prolog compilers, you
can use the shorthands {File}
for logtalk_load(File)
and
{File1, File2, ...}
for logtalk_load([File1, File2, ...])
. The use
these shorthands should be restricted to the Logtalk/Prolog top-level
interpreter as they are not part of the language specification and may be
commented out in case of conflicts with backend Prolog compiler features.
The built-in predicate logtalk_make/0 can be used to
reload all modified source files. With most backend Prolog compilers,
you can also use the {*}
top-level shortcut. Files are also reloaded
when the compilation mode changes. An extended version of this predicate,
logtalk_make/1, accepts multiple targets including
all
, clean
, check
, circular
, documentation
, caches
,
debug
, normal
, and optimal
. For example, assume that you have
loaded your application files and found a bug. You can easily recompile the
files in debug mode by using the logtalk_make(debug)
goal. After
debugging and fixing the bug, you can reload the files in normal mode
using the logtalk_make(normal)
or in optimized mode using the
logtalk_make(optimal)
goal. See the predicates documentation for a
complete list of targets and top-level shortcuts. In particular, the
logtalk_make(clean)
goal can be specially useful before switching
backend Prolog compilers as the generated intermediate files may not be
compatible. The logtalk_make(caches)
goal is usually used when
benchmarking compiler performance improvements.
Compiler errors, warnings, and commentsï
Following a Prolog tradition inherited from Quintus Prolog, the compiler
prefixes (by default) errors with a !
and warnings with a *
. For
example:
! Existence error: directive object/1 does not exist
! in directive end_object/0
! in file /home/jdoe/logtalk/examples/errors/unmatched_directive.lgt at or above line 27
* No matching clause for goal: baz(a)
* while compiling object main_include_compiler_warning
* in file /home/jdoe/logtalk/examples/errors/include_compiler_warning.lgt between lines 38-39
Compiler comments are prefixed by %
. For example:
?- {ack(loader)}.
% [ /home/jdoe/logtalk/examples/ack/ack.lgt loaded ]
% [ /home/jdoe/logtalk/examples/ack/loader.lgt loaded ]
% (0 warnings)
true.
Loader filesï
If you look into the Logtalk distribution, you will notice that most source
code directories (e.g. of tools, libraries, and examples) contain a driver
file that can be used to load all included source files and any required
libraries. These loader files are usually named loader.lgt
or contain
the word loader in their name. Loader files are ordinary source files and
thus compiled and loaded like any source file. By also defining a loader file
for your project, you can then load it by simply typing:
| ?- {loader}.
Another driver file, usually named tester.lgt
(or containing the word
tester in its name) is commonly used to load and run tests. By also
defining a tester file for your project, you can then run its tests by
simply typing:
| ?- {tester}.
Usually these driver files contain calls to the built-in predicates
set_logtalk_flag/2 (e.g. for setting global,
project-specific, flag values) and logtalk_load/1 or
logtalk_load/2 (for loading project files), wrapped
inside a Prolog initialization/1
directive for portability. For
instance, if your code is split in three source files named
source1.lgt
, source2.lgt
, and source3.lgt
, then the contents
of your loader file could be:
:- initialization((
% set project-specific global flags
set_logtalk_flag(events, allow),
% load the project source files
logtalk_load([source1, source2, source3])
)).
Another example of directives that are often used in a loader file would
be op/3
directives declaring global operators needed by your
project. Loader files are also often used for setting source
file-specific compiler flags (this is useful even when you only have a
single source file if you always load it with using the same set of
compiler flags). For example:
:- initialization((
% set project-specific global flags
set_logtalk_flag(source_data, off),
% load the project source files
logtalk_load(
[source1, source2, source3],
% source file-specific flags
[portability(warning)]),
logtalk_load(
[source4, source5],
% source file-specific flags
[portability(silent)])
)).
To take the best advantage of loader and tester files, define a clause for
the multifile and dynamic logtalk_library_path/2
predicate for the
directory containing your source files as explained in the next section.
When your project also uses Prolog module resources, the loader file is also the advised place to load them, preferably without any exports. For example:
:- use_module(library(clpfd), []).
...
:- initialization((
...
)).
Complex projects often use a main loader file that loads the loader files of each of the project components. Thus, loader files provide a central point to understand a project organization and dependencies.
Worth mentioning here a common mistake when first starting working with loader
files. New users sometimes try to set compiler flags using logtalk_load/2
when loading a loader file. For example, by writing:
| ?- logtalk_load(loader, [optimize(on)]).
This will not work as you might expect as the compiler flags will only
be used in the compilation of the loader.lgt
file itself and will
not affect the compilation of files loaded through the
initialization/1
directive contained on the loader file.
Libraries of source filesï
Logtalk defines a library simply as a directory containing source files. Library locations can be specified by defining or asserting clauses for the dynamic and multifile predicate logtalk_library_path/2. For example:
:- multifile(logtalk_library_path/2).
:- dynamic(logtalk_library_path/2).
logtalk_library_path(shapes, '$LOGTALKUSER/examples/shapes/').
The first argument of the predicate is used as an alias for the path on the second argument. Library aliases may also be used on the second argument. For example:
:- multifile(logtalk_library_path/2).
:- dynamic(logtalk_library_path/2).
logtalk_library_path(lgtuser, '$LOGTALKUSER/').
logtalk_library_path(examples, lgtuser('examples/')).
logtalk_library_path(viewpoints, examples('viewpoints/')).
This allows us to load a library source file without the need to first
change the current working directory to the library directory and then
back to the original directory. For example, in order to load a
loader.lgt
file, contained in a library named viewpoints
, we
just need to type:
| ?- logtalk_load(viewpoints(loader)).
The best way to take advantage of this feature is to load at startup a source
file containing clauses for the logtalk_library_path/2
predicate needed
for all available libraries (typically, using a settings file, as
discussed below). This allows us to load library source files or entire
libraries without worrying about libraries paths, improving code portability.
The directory paths on the second argument should always end with the path
directory separator character. Most backend Prolog compilers allows the use
of environment variables in the second argument of the logtalk_library_path/2
predicate. Use of POSIX relative paths (e.g. '../'
or './'
) for
top-level library directories (e.g. lgtuser
in the example above) is
not advised as different backend Prolog compilers may start with
different initial working directories, which may result in portability
problems of your loader files.
This library notation provides functionality inspired by the
file_search_path/2
mechanism introduced by Quintus Prolog and later
adopted by some other Prolog compilers but with a key difference: there
is no fragile search mechanism and the Logtalk make
can be used to
check for duplicated library aliases. Multiple definitions for the
same alias are problematic when using external dependencies as any
third-party update to those dependencies can introduce file name clashes.
Note that the potential for these clashes cannot be reliably minimized by
a careful ordering of the logtalk_library_path/2
predicate clauses
due to this predicate being multifile and dynamic.
Settings filesï
Although is always possible to edit the backend Prolog compiler adapter
files, the recommended solution to customize compiler flags is to create a
settings.lgt
file in the Logtalk user folder or in the user home folder.
Depending on the backend Prolog compiler and on the operating-system,
is also possible to define per-project settings files by creating a
settings.lgt
file in the project directory and by starting Logtalk from
this directory. At startup, Logtalk tries to load a settings.lgt
file
from the following directories, searched in sequence:
Startup directory (
$LOGTALK_STARTUP_DIRECTORY
)Logtalk user directory (
$LOGTALKUSER
)User home directory (
$HOME
;%USERPROFILE%
on Windows if%HOME%
is not defined)Application data directory (
%APPDATA%\Logtalk
; only on Windows)Config directory (
$XDG_CONFIG_HOME/logtalk
)Default config directory (
$HOME/.config/logtalk/
)
The startup directory is only searched when the read-only
settings_file flag is set to allow
.
When no settings files are found, Logtalk will use the default compiler flag
values set on the backend Prolog compiler adapter files. When limitations of
the backend Prolog compiler or on the operating-system prevent Logtalk from
finding the settings files, these can always be loaded manually after Logtalk
startup.
Settings files are normal Logtalk source files (although when automatically
loaded by Logtalk they are compiled and loaded silently with any errors being
reported but otherwise ignored). The usual contents is an
initialization/1
Prolog directive containing calls to the
set_logtalk_flag/2
Logtalk built-in predicate and asserting clauses for the
logtalk_library_path/2
multifile dynamic predicate. Note that the
set_logtalk_flag/2
directive cannot be used as its scope is local to the source file being
compiled.
One of the troubles of writing portable applications is the different feature sets of Prolog compilers. Using the Logtalk support for conditional compilation and the prolog_dialect flag we can write a single settings file that can be used with several backend Prolog compilers:
:- if(current_logtalk_flag(prolog_dialect, yap)).
% YAP specific settings
...
:- elif(current_logtalk_flag(prolog_dialect, gnu)).
% GNU Prolog specific settings
...
:- else.
% generic Prolog settings
:- endif.
The Logtalk distribution includes a settings-sample.lgt
sample file with
commented out code snippets for common settings.
Compiler linterï
The compiler includes a linter that checks for a wide range
of possible problems in source files. Notably, the compiler checks for unknown
entities, unknown predicates, undefined predicates (i.e. predicates that
are declared but not defined), missing directives (including missing
dynamic/1
and meta_predicate/1
directives), redefined built-in
predicates, calls to non-portable predicates, singleton variables, goals that
are always true or always false (i.e. goals that are can be replaced by
true
or fail
), and trivial fails (i.e. calls to predicates with no
match clauses). Most of the linter warnings are controlled by
compiler flags. See the next section
for details.
Compiler flagsï
The logtalk_load/1 and logtalk_compile/1 always use the current set of default compiler flags as specified in your settings file and the Logtalk adapter files or changed for the current session using the built-in predicate set_logtalk_flag/2. Although the default flag values cover the usual cases, you may want to use a different set of flag values while compiling or loading some of your Logtalk source files. This can be accomplished by using the logtalk_load/2 or the logtalk_compile/2 built-in predicates. These two predicates accept a list of options affecting how a Logtalk source file is compiled and loaded:
| ?- logtalk_compile(Files, Options).
or:
| ?- logtalk_load(Files, Options).
In fact, the logtalk_load/1
and logtalk_compile/1
predicates are
just shortcuts to the extended versions called with the default compiler
flag values. The options are represented by a compound term where the
functor is the flag name and the sole argument is the flag value.
We may also change the default flag values from the ones loaded from the adapter file by using the set_logtalk_flag/2 built-in predicate. For example:
| ?- set_logtalk_flag(unknown_entities, silent).
The current default flags values can be enumerated using the current_logtalk_flag/2 built-in predicate:
| ?- current_logtalk_flag(unknown_entities, Value).
Value = silent
yes
Logtalk also implements a set_logtalk_flag/2 directive, which can be used to set flags within a source file or within an entity. For example:
% compile objects in this source file with event support
:- set_logtalk_flag(events, allow).
:- object(foo).
% compile this object with support
% for dynamic predicate declarations
:- set_logtalk_flag(dynamic_declarations, allow).
...
:- end_object.
...
Note that the scope of the set_logtalk_flag/2
directive is local to
the entity or to the source file containing it.
Note
Applications should never rely on default flag values for working properly. Whenever the compilation of a source file or an entity requires a specific flag value, the flag should be set explicitly in the entity, in the source file, or in the loader file.
Read-only flagsï
Some flags have read-only values and thus cannot be changed at runtime. Their values are defined in the Prolog backend adapter files These are:
settings_file
Allows or disables loading of a settings file at startup. Possible values are
allow
,restrict
, anddeny
. The usual default value isallow
but it can be changed by editing the adapter file when e.g. embedding Logtalk in a compiled application. With a value ofallow
, settings files are searched in the startup directory, in the Logtalk user directory, in the user home directory, in theAPPDATA
if running on Windows, and in the XDG configuration directory. With a value ofrestrict
, the search for the settings files skips the startup directory.
prolog_dialect
Identifier of the backend Prolog compiler (an atom). This flag can be used for conditional compilation of Prolog compiler specific code.
prolog_version
Version of the backend Prolog compiler (a compound term,
v(Major, Minor, Patch)
, whose arguments are integers). This flag availability depends on the Prolog compiler. Checking the value of this flag fails for any Prolog compiler that does not provide access to version data.
prolog_compatible_version
Compatible version of the backend Prolog compiler (a compound term, usually with the format
@>=(v(Major, Minor, Patch))
, whose arguments are integers). This flag availability depends on the Prolog compiler. Checking the value of this flag fails for any Prolog compiler that does not provide access to version data.
unicode
Informs Logtalk if the backend Prolog compiler supports the Unicode standard. Possible flag values are
unsupported
,full
(all Unicode planes supported), andbmp
(supports only the Basic Multilingual Plane).
encoding_directive
Informs Logtalk if the backend Prolog compiler supports the encoding/1 directive. This directive is used for declaring the text encoding of source files. Possible flag values are
unsupported
,full
(can be used in both Logtalk source files and compiler generated Prolog files), andsource
(can be used only in Logtalk source files).
tabling
Informs Logtalk if the backend Prolog compiler provides tabling programming support. Possible flag values are
unsupported
andsupported
.
engines
Informs if the backend Prolog compiler provides the required low level multi-threading programming support for Logtalk threaded engines. Possible flag values are
unsupported
andsupported
.
threads
Informs if the backend Prolog compiler provides the required low level multi-threading programming support for all high-level Logtalk multi-threading features. Possible flag values are
unsupported
andsupported
.
modules
Informs Logtalk if the backend Prolog compiler provides suitable module support. Possible flag values are
unsupported
andsupported
(independently of this flag, Logtalk provides limited support for compiling Prolog modules as objects).
coinduction
Informs Logtalk if the backend Prolog compiler provides the required minimal support for cyclic terms necessary for working with coinductive predicates. Possible flag values are
unsupported
andsupported
.
Version flagsï
version_data(Value)
Read-only flag whose value is the compound term
logtalk(Major,Minor,Patch,Status)
. The first three arguments are integers and the last argument is an atom, possibly empty, representing version status:aN
for alpha versions,bN
for beta versions,rcN
for release candidates (withN
being a natural number), andstable
for stable versions. Theversion_data
flag is also a de facto standard for Prolog compilers.
Lint flagsï
linter(Option)
Meta-flag for managing the values of all the linter flags as a group. Possible option values are
on
to set all the individual linter flags towarning
,off
to set all the individual linter flags tosilent
, anddefault
to set all the individual linter flag values to their defaults as defined in the backend adapter files (the usual default). This flag must always be defined in the backend adapter files with the value ofdefault
.
unknown_entities(Option)
Controls the unknown entity warnings, resulting from loading an entity that references some other entity that is not currently loaded. Possible option values are
warning
(the usual default) andsilent
. Note that these warnings are not always avoidable, specially when using reflective designs of class-based hierarchies.
unknown_predicates(Option)
Defines the compiler behavior when unknown messages or calls to unknown predicates (or non-terminals) are found. An unknown message is a message sent to an object that is not part of the object protocol. An unknown predicate is a called predicate that is neither locally declared or defined. Possible option values are
error
,warning
(the usual default), andsilent
(not recommended).
undefined_predicates(Option)
Defines the compiler behavior when calls to declared but undefined predicates (or non-terminals) are found. Note that these calls will fail at runtime as per closed-world assumption. Possible option values are
error
,warning
(the usual default), andsilent
(not recommended).
steadfastness(Option)
Controls warnings about possible non steadfast predicate definitions due to variable aliasing at a clause head and a cut in the clause body. Possible option values are
warning
andsilent
(the usual default due to the possibility of false positives).
portability(Option)
Controls the non-ISO specified Prolog built-in predicate and non-ISO specified Prolog built-in arithmetic function calls warnings plus use of non-standard Prolog flags and/or flag values. Possible option values are
warning
andsilent
(the usual default).
deprecated(Option)
Controls the deprecated predicate warnings. Possible option values are
warning
(the usual default) andsilent
.
missing_directives(Option)
Controls the missing predicate directive warnings. Possible option values are
warning
(the usual default) andsilent
(not recommended).
duplicated_directives(Option)
Controls the duplicated predicate directive warnings. Possible option values are
warning
(the usual default) andsilent
(not recommended). Note that conflicting directives for the same predicate are handled as errors, not as duplicated directive warnings.
trivial_goal_fails(Option)
Controls the printing of warnings for calls to local static predicates with no matching clauses. Possible option values are
warning
(the usual default) andsilent
(not recommended).
always_true_or_false_goals(Option)
Controls the printing of warnings for goals that are always true or false. Possible option values are
warning
(the usual default) andsilent
(not recommended). A unexpected exception in the goal being checked is also reported.
grammar_rules(Option)
Controls the printing of grammar rules related warnings. Possible option values are
warning
(the usual default) andsilent
(not recommended).
arithmetic_expressions(Option)
Controls the printing of arithmetic expressions related warnings. Possible option values are
warning
(the usual default) andsilent
(not recommended).
lambda_variables(Option)
Controls the printing of lambda variable related warnings. Possible option values are
warning
(the usual default) andsilent
(not recommended).
suspicious_calls(Option)
Controls the printing of suspicious call warnings. Possible option values are
warning
(the usual default) andsilent
(not recommended).
redefined_built_ins(Option)
Controls the Logtalk and Prolog built-in predicate redefinition warnings. Possible option values are
warning
andsilent
(the usual default). Warnings about redefined Prolog built-in predicates are often the result of running a Logtalk application on several Prolog compilers as each Prolog compiler defines its set of built-in predicates.
redefined_operators(Option)
Controls the Logtalk and Prolog built-in operator redefinition warnings. Possible option values are
warning
(the usual default) andsilent
. Redefining Logtalk operators or standard Prolog operators can break term parsing causing syntax errors or change how terms are parsed introducing bugs.
singleton_variables(Option)
Controls the singleton variable warnings. Possible option values are
warning
(the usual default) andsilent
(not recommended).
naming(Option)
Controls warnings about entity, predicate, and variable names per official coding guidelines (which advise using underscores for entity and predicate names and camel case for variable names). Additionally, variable names should not differ only on case. Possible option values are
warning
andsilent
(the usual default due to the current limitation to ASCII names and the computational cost of the checks).
duplicated_clauses(Option)
Controls warnings of duplicated entity clauses (and duplicated entity grammar rules). Possible option values are
warning
andsilent
(the usual default due to the required heavy computations). When the term-expansion mechanism is used and results in duplicated clauses, the reported line numbers are for lines of the original clauses that were expanded.
disjunctions(Option)
Controls warnings on clauses where the body is a disjunction. Possible option values are
warning
(the usual default) andsilent
. As per coding guidelines, in most cases, these clauses can be rewritten using a clause per disjunction branch for improved code readability.
conditionals(Option)
Controls warnings on if-then-else and soft-cut control constructs. Possible option values are
warning
(the usual default) andsilent
. Warnings include misuse of cuts, potential bugs in the test part, and missing else part (lack of compliance with coding guidelines).
catchall_catch(Option)
Controls warnings on
catch/3
goals that catch all exceptions. Possible option values arewarning
andsilent
(the usual default). Lack of standardization often makes it tricky or cumbersome to avoid too genericcatch/3
goals when writing portable code.
left_recursion(Option)
Controls warnings of left-recursion on clauses and grammar rules. Specifically, when the clause or grammar rule head and the left most goal in the body are variants. Possible option values are
warning
(the usual default) andsilent
.
tail_recursive(Option)
Controls warnings of non-tail recursive predicate (and non-terminal) definitions. The lint check does not detect all cases of non-tail recursive predicate definitions, however. Also, definitions that make two or more recursive calls are not reported as usually they cannot be changed to be tail recursive. Possible option values are
warning
andsilent
(the usual default).
encodings(Option)
Controls the source file text encoding warnings. Possible option values are
warning
(the usual default) andsilent
.
general(Option)
Controls warnings that are not controlled by a specific flag. Possible option values are
warning
(the usual default) andsilent
.
Optional features compilation flagsï
complements(Option)
Allows objects to be compiled with support for complementing categories turned off in order to improve performance and security. Possible option values are
allow
(allow complementing categories to override local object predicate declarations and definitions),restrict
(allow complementing categories to add predicate declarations and definitions to an object but not to override them), anddeny
(ignore complementing categories; the usual default). This option can be used on a per-object basis. Note that changing this option is of no consequence for objects already compiled and loaded.
dynamic_declarations(Option)
Allows objects to be compiled with support for dynamic declaration of new predicates turned off in order to improve performance and security. Possible option values are
allow
anddeny
(the usual default). This option can be used on a per-object basis. Note that changing this option is of no consequence for objects already compiled and loaded. This option is only checked when sending an asserta/1 or assertz/1 message to an object. Local asserting of new predicates is always allowed.
events(Option)
Allows message sending calls to be compiled with or without event-driven programming support. Possible option values are
allow
anddeny
(the usual default). Objects (and categories) compiled with this option set todeny
use optimized code for message-sending calls that does not trigger events. As such, this option can be used on a per-object (or per-category) basis. Note that changing this option is of no consequence for objects already compiled and loaded.
context_switching_calls(Option)
Allows context switching calls (
(<<)/2
) to be either allowed or denied. Possible option values areallow
anddeny
. The default flag vale isallow
. Note that changing this option is of no consequence for objects already compiled and loaded.
Backend Prolog compiler and loader flagsï
underscore_variables(Option)
Controls the interpretation of variables that start with an underscore (excluding the anonymous variable) that occur once in a term as either donât care variables or singleton variables. Possible option values are
dont_care
(the default for all supported backends) andsingletons
. Although a changeable flag, its value is backend dependent and thus expected to be set only in the backend adapter files.
prolog_compiler(Flags)
List of compiler flags for the generated Prolog files. The valid flags are specific to the used Prolog backend compiler. The usual default is the empty list. These flags are passed to the backend Prolog compiler built-in predicate that is responsible for compiling to disk a Prolog file. For Prolog compilers that donât provide separate predicates for compiling and loading a file, use instead the prolog_loader flag.
prolog_loader(Flags)
List of loader flags for the generated Prolog files. The valid flags are specific to the used Prolog backend compiler. The usual default is the empty list. These flags are passed to the backend Prolog compiler built-in predicate that is responsible for loading a (compiled) Prolog file.
Other flagsï
scratch_directory(Directory)
Sets the directory to be used to store the temporary files generated when compiling Logtalk source files. This directory can be specified using an atom or using library notation. The directory must always end with a slash. The default value is a sub-directory of the source files directory, either
'./lgt_tmp/'
or'./.lgt_tmp/'
(depending on the backend Prolog compiler and operating-system). Relative directories must always start with'./'
due to the lack of a portable solution to check if a path is relative or absolute. The default value set on the backend Prolog compiler adapter file can be overriden by defining thescratch_directory
library alias (see the logtalk_library_path/2 predicate documentation for details).
report(Option)
Controls the default printing of messages. Possible option values are
on
(by usual default, print all messages that are not intercepted by the user),warnings
(only print warning and error messages that are not intercepted by the user), andoff
(do not print any messages that are not intercepted by the user).
code_prefix(Character)
Enables the definition of prefix for all functors of Prolog code generated by the Logtalk compiler. The option value must be a single character atom. Its default value is
'$'
. Specifying a code prefix provides a way to solve possible conflicts between Logtalk compiled code and other Prolog code. In addition, some Prolog compilers automatically hide predicates whose functor start with a specific prefix such as the character$
. Although this is not a read-only flag, it should only be changed at startup time and before loading any source files. When changing this flag (e.g. from a settings file), restart with the clean flag turned on to ensure that any compiled files using the oldcode_prefix
value will be recompiled.
optimize(Option)
Controls the compiler optimizations. Possible option values are
on
(used by default for deployment) andoff
(used by default for development). Compiler optimizations include the use of static binding whenever possible, the removal of redundant calls totrue/0
from predicate clauses, the removal of redundant unifications when compiling grammar rules, and inlining of predicate definitions with a single clause that links to a local predicate, to a plain Prolog built-in (or foreign) predicate, or to a Prolog module predicate with the same arguments. Care should be taken when developing applications with this flag turned on as changing and reloading a file may render static binding optimizations invalid for code defining in other loaded files. Turning on this flag automatically turns off the debug flag.
source_data(Option)
Defines how much information is retained when compiling a source file. Possible option values are
on
(the usual default for development) andoff
. With this flag set toon
, Logtalk will keep the information represented using documenting directives plus source location data (including source file names and line numbers). This information can be retrieved using the reflection API and is useful for documenting, debugging, and integration with third-party development tools. This flag can be turned off in order to generate more compact code.
debug(Option)
Controls the compilation of source files in debug mode (the Logtalk default debugger can only be used with files compiled in this mode). Also controls, by default, printing of
debug>
anddebug(Topic)
messages. Possible option values areon
andoff
(the usual default). Turning on this flag automatically turns off the optimize flag.
reload(Option)
Defines the reloading behavior for source files. Possible option values are
skip
(skip reloading of already loaded files; this value can be used to get similar functionality to the Prolog directiveensure_loaded/1
but should be used only with fully debugged code),changed
(the usual default; reload files only when they are changed since last loaded provided that any explicit flags and the compilation mode are the same as before), andalways
(always reload files).
relative_to(Directory)
Defines a base directory for resolving relative source file paths. The default value is the directory of the source file being compiled.
hook(Object)
Allows the definition of an object (which can be the pseudo-object user) implementing the expanding built-in protocol. The hook object must be compiled and loaded when this option is used. Itâs also possible to specify a Prolog module instead of a Logtalk object but the module must be pre-loaded and its identifier must be different from any object identifier.
clean(Option)
Controls cleaning of the intermediate Prolog files generated when compiling Logtalk source files. Possible option values are
off
andon
(the usual default). When turned on, intermediate files are deleted after loading and all source files are recompiled disregarding any existing intermediate files. When turned off, the intermediate files are kept. This is useful when embedding applications, which requires collecting the intermediate code, and when working on large applications to avoid repeated recompilation of stable code. The flag must be turned on when changing compilation modes, changing flags such as code_prefix, or when turning on linter flags that are off by default without at the same time making changes to the application source files themselves as any existing intermediate files would not be recompiled as necessary due to file timestamps not changing.
User-defined flagsï
Logtalk provides a create_logtalk_flag/3 predicate that can be used for defining new flags.
Reloading source filesï
As a general rule, reloading source files should never occur in production code and should be handled with care in development code. Reloading a Logtalk source file usually requires reloading the intermediate Prolog file that is generated by the Logtalk compiler. The problem is that there is no standard behavior for reloading Prolog files. For static predicates, almost all Prolog compilers replace the old definitions with the new ones. However, for dynamic predicates, the behavior depends on the Prolog compiler. Most compilers replace the old definitions but some of them simply append the new ones, which usually leads to trouble. See the compatibility notes for the backend Prolog compiler you intend to use for more information. There is an additional potential problem when using multi-threading programming. Reloading a threaded object does not recreate from scratch its old message queue, which may still be in use (e.g. threads may be waiting on it).
When using library entities and stable code, you can avoid reloading the
corresponding source files (and, therefore, recompiling them) by setting
the reload compiler flag to skip
. For code under
development, you can turn off the clean flag to avoid
recompiling files that have not been modified since last compilation
(assuming that backend Prolog compiler that you are using supports
retrieving of file modification dates). You can disable deleting the
intermediate files generated when compiling source files by changing the
default flag value in your settings file, by using the corresponding
compiler flag with the compiling and loading built-in predicates, or,
for the remaining of a working session, by using the call:
| ?- set_logtalk_flag(clean, off).
Some caveats that you should be aware. First, some warnings that might
be produced when compiling a source file will not show up if the
corresponding object file is up-to-date because the source file is not
being (re)compiled. Second, if you are using several Prolog compilers
with Logtalk, be sure to perform the first compilation of your source
files with the clean
flag turned off: the intermediate Prolog files
generated by the Logtalk compiler may be not compatible across Prolog
compilers or even for the same Prolog compiler across operating systems
(e.g. due to the use of different character encodings or end-of-line
characters).
Batch processingï
When doing batch processing, you probably want to turn off the
report flag to suppress all messages of type
banner
, comment
, comment(_)
, warning
, and warning(_)
that are normally printed. Note that error messages and messages providing
information requested by the user will still be printed.
Optimizing performanceï
The default compiler flag settings are appropriated for the development but not necessarily for the deployment of applications. To minimize the generated code size, turn the source_data flag off. To optimize runtime performance, turn on the optimize flag. Your chosen backend Prolog compiler may also provide performance related flags; check its documentation.
Pay special attention to file compilation/loading order. Whenever possible, compile and load your files taking into account file dependencies. By default, the compiler will print a warning whenever a file references an entity that is not yet loaded. Solving these warnings is key for optimal performance by enabling static binding optimizations. For a clear picture of file dependencies, use the diagrams tool to generate a file dependency diagram for your application.
Minimize the use of dynamic predicates. Parametric objects can often be used in alternative. When dynamic predicates cannot be avoided, try to make them private. Declaring a dynamic predicate also as a private predicate allows the compiler to optimize local calls to the database methods (e.g. assertz/1 and retract/1) that modify the predicate.
Sending a message to self implies dynamic binding but there are often cases where (::)/1 is misused to call an imported or inherited predicate that is never going to be redefined in a descendant. In these cases, a super call, (^^)/1, can be used instead with the benefit of often enabling static binding. Most of the guidelines for writing efficient Prolog code also apply to Logtalk code. In particular, define your predicates to take advantage of first-argument indexing. In the case of recursive predicates, define them as tail-recursive predicates whenever possible.
See the section on performance for a detailed discussion on Logtalk performance.
Portable applicationsï
Logtalk is compatible with most modern standards compliant Prolog compilers. However, this does not necessarily imply that your Logtalk applications will have the same level of portability. If possible, you should only use in your applications Logtalk built-in predicates and ISO Prolog specified built-in predicates and arithmetic functions. If you need to use built-in predicates (or built-in arithmetic functions) that may not be available in other Prolog compilers, you should try to encapsulate the non-portable code in a small number of objects and provide a portable interface for that code through the use of Logtalk protocols. An example will be code that access operating-system specific features. The Logtalk compiler can warn you of the use of non-ISO specified built-in predicates and arithmetic functions by using the portability compiler flag.
Conditional compilationï
Logtalk supports conditional compilation within source files using the if/1, elif/1, else/0, and endif/0 directives. This support is similar to the support found in several Prolog systems such as ECLiPSe, GNU Prolog, SICStus Prolog, SWI-Prolog, XSB, and YAP.
Avoiding common errorsï
Try to write objects and protocol documentation before writing any other code; if you are having trouble documenting a predicate perhaps we need to go back to the design stage.
Try to avoid lengthy hierarchies. Composition is often a better choice over inheritance for defining new objects (Logtalk supports component-based programming through the use of categories). In addition, prototype-based hierarchies are semantically simpler than class-based hierarchies.
Dynamic predicates or dynamic entities are sometimes needed, but we should always try to minimize the use of non-logical features such as asserts and retracts.
Since each Logtalk entity is independently compiled, if an object inherits a dynamic or a meta-predicate predicate, then the respective directives must be repeated to ensure a correct compilation.
In general, Logtalk does not verify if a user predicate call/return arguments comply with the declared modes. On the other hand, Logtalk built-in predicates, built-in methods, and message sending control structures are fully checked for calling mode errors.
Logtalk error handling strongly depends on the ISO compliance of the
chosen Prolog compiler. For instance, the error terms that are generated
by some Logtalk built-in predicates assume that the Prolog built-in
predicates behave as defined in the ISO standard regarding error
conditions. In particular, if your Prolog compiler does not support a
read_term/3
built-in predicate compliant with the ISO Prolog
Standard definition, then the current version of the Logtalk compiler
may not be able to detect misspell variables in your source code.
Coding style guidelinesï
It is suggested that all code between an entity opening and closing directives be indented by one tab stop. When defining entity code, both directives and predicates, Prolog coding style guidelines may be applied. All Logtalk source files, examples, and standard library entities use tabs (the recommended setting is a tab width equivalent to 4 spaces) for laying out code. Closed related entities can be defined in the same source file. However, for best performance, is often necessary to have an entity per source file. Entities that might be useful in different contexts (such as library entities) are best defined in their own source files.
A detailed coding style guide is available at the Logtalk official website.