Showing posts with label LogicalTypes. Show all posts
Showing posts with label LogicalTypes. Show all posts

Tuesday, December 18, 2012

Logical Types, LLC, Archived News

Archived
News


2007-09-05:
For those of us not familiar with computing with intervals, this Google talk provides an engaging introduction. As Dr. Walster points out, we have become very concerned about speed of computation, but have ignored their accuracy -- sometimes with deadly results. Intervals provide a type-safe, exception-free, approach to numerical computing with built-in feedback on the accuracy. A central repository of interval computing can be found at http://www.cs.utep.edu/interval-comp/
2007-03
29:
I have eliminated my "corrections" to the Mercury extras xml library (I had an outdated version of the library as outlined in the 2007-03-18 news post, so my corrections were redundant the the Mercury team's). At the same time, I have moved my XML enhancements into the utils library and promoted thedoug_graph module from alpha; it is now also included in utils.
18:
A new library and several improvements can be found in the shared repository:
  • The xml library provided in the Mercury extras distribution is out-of-date; it no longer compiles. I have fixed the compile errors and added several modules (to assist in XML transformations and pretty-printing) and tests.
    Update 2007-03-28:
    I was in error in the above item: my copy of the xml library provided by the Mercury team was out of date; the version supplied with the extras distribution by the Mercury team is working correctly.
    This means my fixes to the xml library are redundant, so I withdraw them. The extended functionality, however, I do continue to find (very) useful, so I am moving these enhancements as an xml library under the utils umbrella.
  • Several improvements are available for qcheck2:
    1. I have modified the qcheck2 library so that it now uses the RNG protocol as proposed in the Mercury users' maillist. I have also modified the reporting feature to accept a polymorphic type for module.predicate unit tests ... this improvement 'upgrades' qcheck2 to be an independent library (qcheck was also an independent library).
    2. The program qcpt that generates the module.predicate test points for a system has also been updated to use the new qcheck2 reporting protocol. qcpt is bundled with ltq.
  • I have entirely changed the utils library:
    1. Although useful for a small number of repetitions, the peano module becomes unweildly for large cycles (1,000,000 is represented as 1,000,001 cons cells!). So, I have discarded it in favor of a slightly more sophisticated counting algorithm (where 1,000,000 is represented by 7 cons cells) in the utils.series module that now also includes loop abstraction with func unfold/3 and pred svunfold/6 (the latter being used when one must also update a dependent state variable).
    2. Julian Fondrant on the Mercury Users maillist proposed a RNG typeclass and protocol, and published a module implementation using the tausworthe3 algorithm. I have incorporated this module (as utils.random) with a simplified façade and other minor corrections.
2007-02
14:
  • The dynamic syntax compiler ltq ("Logical Types Quicksilver") and qcpt (the QuickCheck type generator) have been modified to work with the new structure of Melbourn's Mercury ops module. The new system has been updated and is available in our shared repository.
  • The qcheck2 library has been updated to work with the current ltq; it includes two sample modules with different reportage options. It can be found in the shared repository.
  • The peano module has received some new counting functions to simplify the user's task of constructing small peano numbers.
02:
We present a very tiny foreign interface to libtiff. The module tiffany allows reading and writing simple RGBA TIFFs with two-dimensional arrays. As thematrix module matures, we will use that protocol for more comprehensive image filtering. This interface is presented with some samples and (currently) no documentation (other than code comments) as is available from our shared repository. Both libtiff and the matrix protocol must be available to run the samples.
Update 2007-02-06:
Per the suggestions of the Mercury team, I have updated the foreign protocol calls from the old C-specific interface to the generic and supported interface. Also, made the library "more" threadsafe by eliminating the global file pointers and subsuming those values into the image structure.
Update 2007-02-07:
Changing the c_pointer type to a foreign_type pragma (again) to avoid the old C-style foreign interface and also to eliminate unnecessary warnings about int-to-pointer casts.
2007-01
31:
As the Mercury standard library does not have a matrix protocol, I am building a module that handles some basic matrix operations; this module and its test suites will be added to the utils library when in serviceable condition.
30:
Thanks to the efforts of the Mercury research group at Melbourne, their Mercury compiler (mmc) is now available for Macintosh computers with the Intel chipset. This is the alpha ROTD (2007-01-21) as opposed to the currently sanctioned 0.13.1 release, so some things are experimental in this version of the compiler. Email logicaltypes.com if you wish to have a binary distribution.
Our compiler, ltq (that allows syntactic extensions with the op/3 directive, see the news archives at 2006-03-02), may need some work as the Mercury research team has changed the ops module. This, of course, also means that systems that depend on these improvements, such as the test-building framework qcpt (see news archive post 2006-05-12), must also be retested. We will post an update when qld is again tested and working.
2006-07
17:
The various data structures provided in the ROTD extras/ distribution that handles solver types (particularly the various any modes) do not compile because of purity issues; also, since their release, the coding style has changed for module qualification. The fixes to get these types compiled are here. For the Mercury team's review are the diffs to fold back into the distribution.
2006-05
13:
We present a complete rewrite of Mercury's QuickCheck implementation: qcheck2 (the sample unit test module (peano_unit_tests) requires the peanomodule in order to run). The essence of testing à la qcheck -- type discernment to obtain (random) test values with a test specification language -- remains unchanged. The new features of this new version enhance the system with:
  1. complete control over what is reported and when it is reported;
  2. dynamic control over ranges of random number-like values (ints, floats and chars) as well as the random number generator type itself;
  3. and for the code-hacker: qcheck2 is broken up into separate modules along functional lines
The documentation for qcheck (upon which qcheck2 is built) is a model for any system to follow; so, documentation, to include a system description and transition guide, for qcheck2 is under development.
Update 2006-05-20:
As the test description (Description) may now be of any type -- even non-comparable types, such as, for example, function types -- using a map with Description keys is will cause errors when using Description types that cannot be compared. As such, the qstate now uses an assoc_list to accumulate the test results.
Update 2006-05-21:
Added information to the summary report: this report now shows which predicates were not tested. The work-in-progressdocumentation now covers the reporting facility comprehensively.
12:
ltq (Logical Types Quicksilver compiler) now comes with qcpt (QuickCheck Predicate Types), a system that discerns the interface predicates and functions of a system (to facilitate comprehensive unit testing). Perhaps even more important is the inclusion of a README that doubles as a HOWTO and INSTALL document.
11:
Completed the document describing an implementation of mutable syntax for Mercury. This article in available in the literature section (see above).
2006-04
30:
The graph module takes a rather non-directed approach to locating a path from Node1 to NodeN. This is fine if the path has no associated cost, but if one is looking for the best path (where a path has an associated cost), this laissez-faire approach becomes problematic.
As I often need a efficient path in a graph, I have added best_path/5 and other supporting predicates that cannot be implemented well operationally given the protocol of module graph and submitted these changes to the Mercury team for review. While they consider these changes, I provide the implementation here as module doug_graph, with an associated example. Locating the best path in the given example using the naive pathimplementation took over 20 seconds; the reimplementation here completes the computations in less than a second.
27:
The following Mercury compiler distributions are available from here on request:
  • mercury-2006-04-26-rotd-powerpc.apple.darwin8.5
  • mercury-2006-04-26-rotd-sparc.sun.solaris2.8
These distributions include the extras/, the samples/ (in extras/) and the HTML documentation (in doc/).
2006-03
29873210165432108:
The Mercury development team has fixed the problem with nesting of disjunctive terms when output with term_io.write_term/4with ROTD-2006-03-07 compiler distribution. It, along with the op/3 declaration enhancement (ltq), are available from us. Send an email (see contact information below) if you wish to obtain one of the following distributions:
  • mercury-2006-03-25101854310789-rotd-powerpc.apple.darwin8.35
  • mercury-2006-03-287210185431079-rotd-sparc.sun.solaris2.8
These distributions include the extras/, the samples/ (in extras/) and the HTML documentation (in doc/).
0346:
term_io.write_term/4 does not properly handle disjunctions. I've submitted a patch to the Mercury development team, but in the interim, I've included the change in distributions (as dopp relies on write_term/4 to output the op/3-free code results). Also,compiler/prog_io.m has quite a few disjunctions handling (now dead) declaration types, I've submitted this patch to the Mercury team, but have also patched local distributions. The most recent distributions available are:
  • mercury-2006-03-01-rotd-fixed-write_term-sparc.sun.solaris2.8
  • mercury-2006-03-0134-rotd-fixed-write_term-powerpc.apple.darwin8.3
Both these distributions have ltq included. Email me if you wish a copy of one of the above distributions or of the stable release (0.12.2) on either architecture.
Update 2006-03-08:
The Mercury team has corrected the compiler so that terms are appropriately parenthesized. This eliminates the need for my term_io.write_term/4 hack, so future releases of the distributions from this site will revert to straight-up Mercury. Enhancements, such as op/3 declarations, will be included inextras/ (the source code) and in bin/.
02:
There is quite a debate going on at the developers discussion forum as to the merits, extent and implementation of the op/3declaration. Up to this point, we have patched the Mercury compiler distribution so that it accepts and processes op/3 declarations. This has proved to be rather onerous as new distributions have been coming out regularly. Instead, prompted by the Mercury team, I have developed an op/3 preprocessing system. ltq  ("Logical Types Quicksilver") creates build/Makefile, then executes the Makefile which calls dopp  ("Dynamic Op PreProcessor") which translates op/3-enhanced files into plain vanilla Mercury ones. The Makefile then builds the executable with the 'mmc --make --infer-all ' command. This build system is locally available from this site, and also included in Mercury distributions that we produce from this month onward.
Update 2006-03-10:
I have updated ltq so that it takes any number of arbitrary command-line arguments (these preceed the argument). These arguments are passed, unmolested, to mmc.
Update 2006-03-13:
Currently, we are fully integrating op/3 declarations into working products. In building auxilary libraries we have found that we need to mirror mmc's library-building indicator (prepending 'lib' to the target module's name). Adding this functionality required a modification to ltq's build process. This change is reflected in build system offered here. We leave the library installation process to mmc; that is, noddingly: in the generated Makefile, there is an command that passes the proper arguments to mmc with a make install command.
Update 2006-03-24:
The library building and installation process differed slightly in ltq. Eliminated that difference and published a new version of this system.
Update 2006-03-29:
ltq now automatically creates the build/ directory (the repository of build products, including the files converted from op/3-enhanced sources to canonically represented sources). This eliminates errors inltq's build process when it cannot find the nonexistent directory. The new distribution is available, as always, here.
2006-02
17:The bleeding-edge releases of the Mercury compiler (the ROTD (release of the day) 2006-02-11), both for standard Mercury and the Quicksilver enhancements from Logical Types (op/3 declarations and binary-trees and -sets with externalizable structure) are compiled and available for Mac OSX.4 systems. These releases are alpha quality, but do contain several interesting developments, such as improved constraint logic programming syntax,injections (bi-directional maps) and improved term-as-XML handling. Please email if you wish to obtain a copy (see contact information).
Update 2006-02-19:
The Quicksilver distribution has been updated to the ROTD-2006-02-1618.
Update 2006-02-268:
There are a new set of distributions available:
  • ROTD-2006-02-235 distribution now available for sparc.sun.solaris2.8
  • ROTD-2006-02-23 distribution for powerpc.apple.darwin8.3
  • Quicksilver ROTD-2006-02-25 distribution for powerpc.apple.darwin8.3 and for the sparc.sun.solaris2.8 as well
2006-01
31:Quicksilver version 0.12.2 for Mac OS-X.4 is available along with installation instructions.
Update (2006-03-04):
I have removed all distributions from this repository. Please email me if you wish to obtain a copy of any distribution mentioned here.
31:Quicksilver version 0.12.2 for Solaris 8 is available along with installation instructions.
31:Logical Types, LLC is no longer producing binary distributions of Mercury that do not support op/3 declarations. Quicksilver (Mercury with op/3 support) is a strict superset of Mercury and what Logical Types produces.
16:I've posted a review of the PADL-2006.
06:Here is a module that prints out a parsed term in its canonical form (write_canonical.m; useful for inspecting terms with op/3 syntax) and a test module (test_op.m) that exercises write_canonical. This can be used with the regular Mercury compiler, but is designed for Quicksilver compiling modules with embedded op/3 declarations.
04:Mercury compiler enhanced with op/3 declarations for both PowerPC architecture Mac OS-X.4 systems (with installation instructions) and Sparc architecture Solaris 8 systems (with its own installation instructions) available for download. The compiler executable is named "lqc" for "Logical TypesQuicksilver Compiler". A supporting document, detailing the changes to the compiler to permit users to insert op/3 declarations (followed eventually by the typed operators in context) into module implementations, will follow in short order.
Update (2006-01-31):
These distributions (and the ones mentioned below) are obsolete. Email me for the current stable release.
2005-12
27:Mercury compiler for Sparc architecture Solaris 2.8 systems available for download. See the installation instructions to get Mercury running on your SPARC.
24:Mercury compiler for PowerPC architecture Mac OS-X.4 systems available for download. See the installation instructions to get Mercury running on your Mac.

Logical Types Libraries

Libraries, etc.

Logical types has the following libraries, systems, and sample code. All provided with the usual caveats (they are not guaranteed to work, and Logical Types is not liable for you downloading and using this code):
 
Libraries
utils
a set of utilities useful for building production systems
contains modules:
Moduletest statusdocumentation
utils1/4 submodules testedno
utils.graph0/1 new preds testedno
utils.random0/10 preds testedno
utils.series3/3 preds/funcs testedno
utils.xml2/3 submodules testedno
utils.xml.facade1/1 pred testedno
utils.xml.pprint2/2 preds testedno
utils.xml.transform4/9 funcs/preds testedno
qcheck2
A testing/verification framework for Mercury programs
see qcheck2 justification (work in progress)
 
Systems
ltq
Extends Mercury with op/3 declarations, allowing syntax modification
see installation instructions
see ltq doc
see write_canonical that interprets op/3 declarations

sampleshello.m and play.m
 
Fixes/Patches
anys
A set of fixes to the utilities provided in the Mercury extras distribution supporting operations with the any mode
see anys-diffus.zip simply to apply patches
 
AlphaYou Have Been Warned!
matrix
Work in progress to add a viable matrix protocol; will be rolled into the utils library.
tiffany
A very small, thread-un-safe, foreign interface to libtiff; also requires matrix (which it already bundles).
 

Installation Instructions for QuickSilver

Installation Instructions for Quicksilver
PowerPC/Apple architecture
  1. Decompress the archive in a directory of your choice (alias==$dir)
  2. Modify the script mmc in directory $dir/quicksilver-0.12.1.powerpc-apple-darwin8.3/bin so that the MERCURY_COMPILER variable points to$dir/quicksilver-0.12.2.powerpc-apple-darwin8.3/lib/mercury/bin/powerpc-apple-darwin8.3/lib/mercury_compile and so that theMERCURY_CONFIG_DIR variable points to $dir/quicksilver-0.12.2.powerpc-apple-darwin8.3/lib/mercury
  3. Export the following environmental variables with the following values:
    MERCURY_HOME$dir/quicksilver-0.12.2.powerpc-apple-darwin8.3
    MERCURY_STDLIB_DIR$MERCURY_HOME/lib/mercury
  4. Add the following paths to your DYLD_LIBRARY_PATH environmental variable:
    $MERCURY_STDLIB_DIR/lib/reg.gc/powerpc-apple-darwin8.3
    $MERCURY_STDLIB_DIR/lib/powerpc-apple-darwin8.3
  5. Add the following path to your PATH environmental variable:
    $MERCURY_HOME/bin
  6. Add the following path to your MANPATH environmental variable:
    $MERCURY_HOME/man
  7. You should be able to do the following with the file hello.m:
    $ mmc --make hello
    $ ./hello
  8. Since this compiler allows op/3 declarations, the following module, play.m, demonstrates this capability. I intentionally left out some declarations, so compiliation is slightly different:
    $ mmc --infer-all --make play
    $ ./play
Creating syntax with op/3 can become complicated when several operators interact to create a term. I've provided a module that prints the canonical representation of a parsed term (write_canonical.m) and a testing module (test_op.m) that allows prototyping of operator declarations and allows submitting terms under that syntax. The whole test system may be built in the usual way:
$ mmake test_op.depend
$ mmake test_op

Logical Types Company Mission Statement



Rule-based
Systems

Rule-based systems process data according to sets of constraints established by the user. The results of these systems is a collection of rule findings that can be used to construct the final product or to assist the user in rendering an informed decision.
The above description may seem all-too-general, as that describes the what most programs do. This is indeed correct: rule-based programming is sufficiently powerful enough to describe any computable system. In fact, the rule-based approach is now being viewed by the mainstream as the preferred method for workflow analysis, process, resource scheduling, service-based systems, etc. Nearly every software system has a set of rules, explicit or implied, to which it adheres. The rule-based programming style model these rule constructs directly and facilitate their manipulation as the system grows and changes.
Deductive
Logic

The traditional approach to building logic systems is to construct a set of clauses where the head of the clause matches a condition and the rest of the clause verifies the match through a set of goals for that match. These clauses are known as rules and a set of rules is a predicate, and this predicate can be used, in turn, as a goal in a new rule. Deductive logic matches problem specifications very closely and is a very effective way to convert a set of requirements into a production system. Some examples of types of problems that are easily modelled in deductive logic are expert systems, planning systems and scheduling systems.
Deductive systems are used when the rules are clear, when the user requires certain outcomes, and are very good at "explaining" what the rule findings are and how they were arrived at.
Inductive
Logic

The opposite approach to a deductive system is an inductive one. Whereas in an deductive system, the user has very exacting control over the process and outcome, in an inductive system, the rules are obtained by deriving the relations between input data and their outcomes, with very little guidance, if any, from the builder of the system. Traditional inductive systems required very clean data and had little tolerance for deviation -- a slight perturbation in the data set could cause the system to fall into an undefined state. Modern inductive systems have taken a different approach: reaping the benefits of recent advances in probability and statistics, these systems (such as Bayesian systems and neural networks) are highly redundant and adaptive. These new systems consistently perform well: they have excellent success narrowing to a classification from apparently unrelated attributes, and they have a high rate of stability, being very fault-tolerant, even in the presence of very noisy data. It is also trivial to convert a statically trained inductive system to one that learns continuously from new inputs and outcomes.
Inductive systems are used when users cannot explain how they arrive at decisions (attributing the outcomes to a "feel" for the situation), and where gradual trends result in eventual changes to outcomes. These systems excel at making the correct decision with a very high degree of confidence, but are poor at explaining what prompted the decision.
Our Approach
Logical Types, LLC uses both deductive and inductive logic to build systems as the needs of the customer demand:
  • To rediscover the implicit rules of a phoneme-based name matching system, we created a purely inductive system that output a new program that had the phoneme contruction rules explicit.
  • A human resource scheduling system that required a set of clearly stated rules that filled duty times with a pool of personnel under a guiding principle of "fairness" was built using purely deductive logic.
  • A combined learning system and knowledge-engineered rule-based expert system was designed using a deductive rule manager with a supervised learning, Bayesian-like, component.

Testing with qcheck, version 2.0


qcheck, version 2.0

Introduction

QuickCheck is a system developed by John Hughes and Koen Claessen. Its premise is that comprehesive testing can be obtained by combining a testing specification language and test data automation ("random testing"). It has been ported to several languages from its native Haskell: the Mercury programming language provides a QuickCheck facility in their "extras" distribution, called qcheck.
As it stands, the original qcheck (herein after referred to as Q1) is an excellent piece of work, fully capable of testing entire systems. It can decipher how to generate example data from user-defined types. It also provides examples for various general and specific situations where the user may wish to exercise control over the ranges or frequencies of the data generated. What, then, would need to be changed?
Not much, it turns out. The fundamental aspects of the system -- comprehesive unit testing fed by randomly-generated values and directed by a test specification remain intact, but there are several features to comprehensive testing that can be added to improve this system. Q1 is good at verifying that one particular predicate behaves as it should, but it gives no indication that all the (interface) predicates of a system have been tested. Further, the user has some control over the detail reported for each test, but, for a user wishing a summary report, even the smallest report allowed is too much detail: adding a facility that give the user complete control over reportage becomes necessary for larger systems. Finally, Q1 has an excellent facility to generate random values for user-defined types, but for primitive types (such as charint, and float), the approach is a bit arcane (only mentioned in one of the last examples) and lossy -- giving the user control over the random number generator itself, and integrating that part of the system with the goal of ease of use will carry forward the automation of user-defined typed values as well as simplify controlling ranges of generated primitive values.
These improvements were the aim of this new version: keep the essence of previous version while adding these new layers to help the tester verify much larger systems. The first two aims outlined above, that of comprehension and detail, are addressed by the new reporting facility that exists both outside qcheck2 proper and is also integrated into its state. The third aim, more and simpler control over randomly-generated values, incorporate changes that now allow the user to define their own random number generator (or to use the very excellent one supplied) and also to change the generator's behavior in the midst of testing. We will address each aim, and their implementations, in the following sections.

Aims

Aim 1: Comprehension

One question that testing frameworks, such as QuickCheck, must eventually address, particularly for larger systems, is the one of completeness. Or, "has the entire functionality of the system been tested?" Under the first implementation this is a difficult question to answer, and the root of this problem is a rather trivial one to fix: the second input argument toqcheck/[4,7,8] is a string that has the purpose of describing the test to be performed. Typing this argument as a string is rather limitting: although the (human) user can seen the purpose from the description, the problem is that encoding the test description as a string does not facilitate reasoning about test results mechanically, which makes it difficult for the system to report on what was and, importantly, was not, covered in the testing.

A new accumulated state variable

The fix to this problem is therefore simple: generalize the test description argument to any (univeral) type. This way other systems may, for example, generate a model of the system being tested and then, after the tests are completed, a method of tabulating and reporting the results. The internals of Q1 change in two ways: first, the description type signature is relaxed, and second, as reportage is spread across several calls to qcheck/[4,7,8], we will defer the commitment of outputting results until the user so determines. In this case, we convert the state variable from io.state (which commits the output) to a specific accumulator (that simply collects it):
:- pred qcheck(T, Desc, int, list(list(frequency)),
               general_frequencies, list(user_gen_type(RNG)),
               qstate(RNG, Desc), qstate(RNG, Desc))
        <= (testable(T), random_number_generator(RNG)).

:- mode qcheck(in, in, in, in, in, list_skel_in(user_gen_inst(RNG)),
               in, out) is det.
where:
Tthe type (pred/func) to test
Desca generic type describing the test
intnumber of tests
list(list(frequency))list of specific frequencies (as per Q1)
general_frequencieslist of general frequencies for testing (described later)
list(user_gen_type(RNG))user-defined types for value generation, as per Q1, specialized on the random number generator
qstate(RNGDesc)The state variable collecting test results and keeping the random number generator au courant
The typeclass testable(T) is as per Q1; and the random number generator typeclass (random_number_generator(RNG)) will be discussed under the value generator aim.
For the new types for qcheck2:
:- type general_frequencies == assoc_list(type_desc, list(frequency)).
:- type qstate(RNG, Description)
        ---> qstate(generator(RNG), map(Description, test_results)).
:- type test_results
        ---> test_results(int, int, int, bag(univ))
        ;    falsifiable(univ).
The above qstate type shows that the test description is mapped to the test_results type, as qcheck/[4,7,8] is called for each test, it accumulates the test results indexed by the (generic) description.

The Program Modelling Tool

As Description can be of any type, we can now allow the user to pass in a simple description string, as in Q1, or, alternatively, we can enhance the description with something that we can use as a declarative index. In Prolog, we could use the index of the module-qualified name of the predicate. Mercury is not as dynamic a system to allow any number of module/predicate/function values for Description,1 so we will employ a preprocessing system to identify and alias all interface predicate/function types to a uniform (enumerated) type. One such system is qcpt available from logicaltypes.com packaged into the ltq system. From an input set of files, qcpt generates the following types:
:- program_module
        ---> .
:- func all_program_modules = list(program_module).
:- func all_public_predicates = list(full_predicate_signature).

:- type public_predicate_signature
        ---> public_predicate / int.

:- type full_predicate_signature
        ---> public(program_module, public_predicate_signature).

:- type test_desc == pair(full_predicate_signature, string).

:- type public_predicate
        ---> .
Example
Say we wish to test a single module, for example the peano module. By running ...
$ qcpt peano.m
... the file qcheck2.tests_digests_types.m is generated with the above type declarations and the following specific realizations:
% ...

:- type program_module
        ---> peano.

% ...

:- type public_predicate
        ---> increment
 ;    peano
 ;    to_peano.

:- implementation.

all_program_modules = [
 peano
].

all_public_predicates = [
 public(peano, increment/2),
 public(peano, peano/2),
 public(peano, to_peano/2)
].

A new reporting facility

Given that we now have collected all the public (or interface) predicates and functions of a system, it is now also possible to determine the completeness of the testing, in that all of those predicates were tested. It simply now falls to a system to collect the results of testing and report the results. The user may attack this task any number of ways, but we also provide one implementation in module qcheck2.tests_digest_reports. The predicate
show_module_tests_summary(Results, !IO)
does all this work by comparing the accumulated test Results (typed as an assoc_list(test_desc, test_results), where test_desc is provided by moduleqcheck2.tests_digest_types (generated from the tested module using qcpt), and the assoc_list is the accumulated result of calling a set of qcheck/[4,7,8] goals).
The show_module_tests_summary/3 predicate reports the total number of tests (and those that passed) within each module and reports the number of predicates that succeeded all unit testing. This predicate also reports, by name and arity, the predicates not tested by the framework.
Example
The following report ...
$ ./peano_unit_tests_missing_to_peano

Qcheck2 tested the following modules:
*** 6/6 tests passed on 2/3 predicates in peano
    (did not test to_peano/2)
... shows that my modifications to module peano_unit_tests (I commented out the to_peano/2 tests) resulted in all tests passing, but the test suite did not provide full coverage of the peano module's functionality.

Traditional Reportage

Module qcheck2.reports provides two ways to report results as Q1 did:
show_test_results_sets(Results, !IO)
show_test_results(Description, test_results, !IO)
The predicate show_test_results_sets/3 simply iterates over Results, calling show_test_results/4 at each iteration. The predicate show_test_results/4 reports the results from qcheck/[4,7,8] in the style of Q1.
Example
Here are some of the results reported from running peano_unit_tests_with_failing_test with a call to show_test_results_sets/3:
...
1)
Test description : public(peano, increment / 2) - "Makes sure we get a successor"
Number of test cases that succeeded : 100
Number of trivial tests : 0
Number of tests cases which failed the pre-condition : 0
2)
Test description : public(peano, increment / 2) - "Checks increment/2\'s compare"
Number of test cases that succeeded : 19
Number of trivial tests : 0
Number of tests cases which failed the pre-condition : 81
Distributions of selected arguments :
13 {0, 1}
5 {1, 2}
1 {2, 3}
3)
Test description : public(peano, to_peano / 2) - "Tests string creation"
Falsifiable :
i(-68)
...
The above examples demonstrate 1) a sample test report where all the tests succeeded (with no reportage on the test values used), 2) a sample where some test values were unusable (with reportage), and 3) a sample where the predicate and test values (by intention) failed the test. These tests are of the same form as those of Q1. The major difference is that test tests are reported separately from the qcheck/[4,7,8] call. This separation allows delayed (even permanently delayed) reporting of results.

Aim 2: Generation

Q1 offers a dizzying array of options when giving the user control over what test values are generated and how they are generated, that is, if these values are to be generated from user-defined, or complex (not builtin), types. It also offers a generator of such power that it is dubbed: "The Mother of all Random Number Generators" by its creator. Impressive as the above offerings are (and they are, in fact, impressive) there are two glaring wants: first, one is required to use the supplied generator -- users are not permitted alternate generators for their own testing, and this becomes an issue when, for example, "pure" randomness (such as the values provided by random.org), or cryptological-strength randomness is a requirement of the tested system, and, second, restricting the ranges of primitive builtins (or other "unbound" enumerated types (such as integer)) is not directly feasible (albeit possibly with some esoteric indirection). We address each of these wants in the new system in this document by turns.

Want 1: User-supplied random number generators

Q1 was built to hide its inner workings. This software principle is considered to be good practice, but, since one of the inner workings is random number generation to generate test values, this good practice obstructs the ability to replace the random number generator when so required. The new architecture in qcheck2 exposes the random number generator in the state variable, allowing its replacement by alternate equivalent types. The change also comes with a supplied state initialization predicate that provides the default random number generator.
The design of the system requires the user to wrap their random number generator (hereinafter referred to as the RNG) in type described in the next section, and it must conform to the following typeclass:
:- typeclass random_number_generator(RNG) where [
        % gives a random float on the range [0, 1)
        pred rnd(float::out, RNG::in, RNG::out) is det,
 pred reseed(int::in, RNG::unused, RNG::out) is det
].
where:
rnd/3supplies a float between 0.0 and 1.0 and updates the state of the RNG; and
reseed/3reinitializes the RNG with the seed supplied as the first argument.

Want 2: User-controllable numeric ranges

It is very easy for the user of Q1 to specify a discriminated-union type and have the system generate test examples. The problem is for builtin types: Q1 does not consider bounds when generating floats, ints and, surprisingly, chars.2 One could argue that by choosing unbound types, the user must be prepared to accept any value those types describe, and indeed this is true. Where this argument falls apart is when the user wishes to test predicates within their nominal ranges. Certainly the test framework should generate test data that tests beyond the ranges, but when every test case offered is extrinsic, nothing is gained by submitting that predicate to random testing. In short, the user must be allowed direct control over ranges of test values of builtin types when the situation warrants.
:- type generator(RNG)
        ---> generator(int_range, float_range, character_type, RNG).
where:
int_rangeis eitherrange(int, int) or unbound

Endnotes

1
Well, this statement is not true in all cases -- if all the tested predicates had the same type and modality then one may simply use the name of the predicate as the Description. See, for example, modules foo and foo_unit_tests included in the distribution. The material point here is that although it is possible to use the name of a predicate as the Description it is not realistic given that most systems use predicates with different types, arities, and modalities.
2
Q1 treats strings as list(int), where each element is a char-equivalent int, which can be positive, negative or, as is most often the case, of very large magnitude.


(article posted circa 2006)

Mutable Syntax in the Mercury Programming Language


Introduction

The Mercury programming language is a compiled, strict, pure, type-safe logical and functional programming language. Its programming methodology is based on predicate logic, with syntax and semantics in line with that of Prolog. Along with logic, it has a fully integrated Hindley-Milner type system with lambda-terms, very much in the style of the Haskell programming language.
The smooth integration of Prolog with Haskell sounds like a marriage of programming paradigms to release the programmer into Coding Nirvana, as it were. This is how it is for most cases, as the language has a consistent design philosophy backed up by well-researched principles and explained by copious and clear documentation with numerous practical examples. What remains are niche constructs, that is: constructs that may be helpful for specific problems, but are not strictly necessary, nor generally applicable.
One such niche construct, one that I turn to quite often when programming in Prolog, is the op/3 declaration, or, the ability to introduce new syntax into the language so that I may model the problem more naturally. This document covers extending the language to include the op/3 declaration in its full breath of functionality.

Alternatives, and Raison d'être

The approach we take is the modify the compiler so that it accepts the op/3 directive in a module and thereafter, within only that module, parse the operator declared with the specification and priority given in the directive. This may seem like a drastic measure, so we must consider the alternatives before choosing this course of action. There are basically four viable, albeit inferior, alternatives:
  1. The Mercury programming language provides the grave syntactic construct which converts the standard prefixed call to an infix one:
    fn(X, Y) becomes X `fn` Y
    See, for example, the pprint module, as it used to make extensive use of this style (until it recently deprecated this approach to use one of the builtin operators, instead). Just as the Mercury language developers have discovered, this approach has at least two drawbacks:
    1. these infix "operators" are clearly marked as second-class citizens, unnecessarily lengthening what is supposed to be a concise representation;1 and,
    2. only binary infix operators are allowed under this syntax; I often find it useful to type values using a postfix type.
  2. One could construct a specialized instance of the op_table typeclass from the ops module and thereafter use read_term_with_op_table/4 from the term_io module to parse strings at runtime. See samples/calculator2.m provided with the distribution for an example program that demonstrates this approach.
    This approach also has its own set of associated problems:
    1. Constructing one's own op_table is excessive when using only a few operators and tedious when introducing many operators. This manual process steals precious time away from program development that addresses the problem, itself.
    2. Until now, there was no "cookbook" approach addressing the problem of how to create a mutable syntax. The ops module and the sample calculator program are well-documented and provide good examples of how to implement static syntax, but provide no guidance for constructing dynamic, mutable, syntax. For this, one had to design such a framework from first principles.2
    3. An user-defined op_table instance may only be used at runtime. The Mercury compiler, as implemented, does not allow such tables during module compilation.
  3. Third, use one of the available scanners (such as samples/lex/) or parser generators (such as samples/moose/) to create a language syntax-aware preprocessor that substitutes operators and their arguments with the well-formed term replacement. Problems:
    1. This is highly redundant and fruitless exercise, as the compiler has its own parse phase that does the same work, and with the language itself in flux (as is the case for any living language) changes to the syntax quickly render a system created by these means obsolete. Parser generators for other programming languages provide complete grammars for every version of the host programming language, Mercury has no parser generator with such grammars, so this task is left to a user of these kinds of systems.
    2. Furthermore, although the domain-specific languages for these tools closely follow the Mercury programming language to do their work, they do have their own languages that require time and effort to master. When presented with powerful parsing facilities built right into logic programming languages (I'm referring specifically to Definite Clause Grammars (DCGs)), one must weigh the costs of learning these languages before embarking on such an endeavor.
  4. Worst for last: as with C/C++, create a specific preprocessor that parses the source file, converting annotated operators to equivalent Mercury terms by following the preprocessing directives. This approach requires so much work (the C preprocesser is a compiler-sized program) and has so many known pitfalls (such as replacing elements inappropriately (in a quoted string, for example) and causing an unacceptable disjunction between the generated executable and the original source base (confusing debugging and error reporting efforts), that it should not receive serious consideration;3 it does not in this document, at any rate.
By embedding the op/3 syntax into the compiler, the changes we make are hygenic in that they are part of the language syntax, not external and blindly unaware of it, as is the case with with C preprocessor and immediate so that they may be used at compile time in the module in which they are declared. This implementation also limits the lexical scope of the operatorwithin the module in which it is declared,4 preventing these declarations from corrupting modules that eventually use modules with specialized syntax.
The desired state is to integrate the op/3 declaration fully into the the language, so that, e.g., facts may be stated in their vernacular and still be compiled into executable content in the Mercury idiom, as in this real-world example:
for the open weekly timecard ending date(2006, 1, 6):
  employee cgi_emp_001 billed [
      3 hours on sunday - date(2006, 1, 1),
      16.5 hours on monday - date(2006, 1, 2),
      5 hours on tuesday - date(2006, 1, 3),
      5 hours on wednesday - date(2006, 1, 4)
  ] against contract lt_2005_001.
Far from being a contrived pedagogical example,5 the above illustrates the various typing uses of op/3 defined syntax, both prefix ('employee cgi_emp_001') and postfix ('3 hours'). The above fact is certainly "only" a data term (in fact, as well as being a data term, the above fact also contains op/3-based data terms), but fully actualized operators exist as well; the Prolog syntax module is rife with such examples. These uses of op/3-declared syntax (describing entity relationships clearly and as activated syntax) are in no way limited to the rather straightforward problems of accounting, but are also used in production expert systems handling over 1,000,000 transactions per day; the use of these extensions are tied directly to rule findings satisfying customer requirements.
In short, op/3-declared syntax is used extensively in production systems built using Prolog serving real-world requirements under heavy demands. With the preexisting extensions for purity, typing, and functional programming, imagine the utility and expressivity that could be obtained with Mercury so extended!

Implementation

Now that we have justification for modifying the compiler, nothing remains but to get to it. Fortunately, the Mercury compiler, after some study, yields a straightforward implementation approach.
First things first: the ops module uses a discriminator (the type category) to choose among different uses for an operator (e.g. unary '-' verses binary '-'). This discriminator is internal, and, as we need the same functionality when defining new operators, so we externalize that type in library/ops.m by moving the type declaration from the implementation section to the interface.
Given this type, we now decorate predefined (inflexible) op table that will permit additional syntax declarations. For this, we need to index the operator and its category to the syntax declaration, and then make this new type an op_table (typeclass) instance ... we add this type to the interface of compiler/prog_io_util.m:
:- type op_map == map(pair(string, ops.category), op_info).
:- type mercury_op_map ---> mercury_op_map(ops.table, op_map).
:- instance ops.op_table(mercury_op_map).
To further support the new type, we need information against which we index, and we need supporting predicates to construct the information for the parser when encountering the operator (the declarations for this also go into the interface of compiler/prog_io_util.m):
:- type op_info ---> op_info(ops.specifier, ops.priority).
:- func op_specifier_from_string(string) = ops.specifier.
:- func op_category_from_specifier(ops.specifier) = ops.category.
The op_specifier_from_string function simply takes an input string, e.g. "xfx", and coverts it to the equivalent specifier representation, e.g. the functor xfx. Theop_category_from_specifier function follows the (implied) convention of the ops module, which is all prefix specifier types (including binary prefix) are the before categoryand all other specifier types (one of several different infix and postfix possibilities) are the after category type. The complete set of changes are enumerated explicitly in the email on the implementation.
After we augment the functional of the ops module, we need to integrate this into the compiler's parser module (which is actually called prog_io). The efficacious point is where the parser works at the module level,6 this occurs, after some initialization in read_module/11, in read_all_items/7. We initialize the op map here (with a call to init_mercury_op_map), and then pass along that nascent syntax map to the calls that parse the items in the module (by modifying the signatures of read_first_item/9 and the recursive callsread_items_loop_2/11 and read_items_loop/10).
So, for example, read_items_loop/9 becomes:
read_items_loop(ModuleNameSourceFileName!Msgs, !Items!ErrorSyn0!IO)
... where Syn0 is the new syntax map. This map is initialized in the new read_all_items/7 before calling read_items_loop/10 with the goal:
init_mercury_op_map(init_mercury_op_table, Syntax)
The magic occurs in module parser's read_term_with_op_table/5 (called via read_item/7) which normally scans and parses the items in the module. When it encounters anop/3 declaration, however, it eventually resolves to the process_decl/8 back in the prog_io module, which reads the declaration and then adds the syntax declaration to the op map, enhancing the syntax for the current module.
When read_all_items/7 completes its iteration on a module's items, it exits, discarding the op_map instance and any syntax it accumulated from op/3 declarations in that module, returning the compiler to the base, Mercury-defined, syntax. So that the "next" module starts fresh without syntax from other modules polluting the compilation.

Reconsideration

"Worse is Better"7
After some discussion on the Mercury maillist, it was resolved that dynamic syntactic extensions should be external to the compiler. So, Logical Types has developed separate compiliation system that converts modules with syntactic enhancements to plain-jane Mercury equivalents. For modules with no syntactic enhancements, `ltq' is equivalent to `mmc --make --infer-all'. For modules with op/3 declarations in the implementation, `ltq' first parses the module and writes out all terms canonically. After this translation, the system compiles the modules into the resulting executable or library.

Operation

This system reduces rather nicely by using facilities provided by the Mercury compiler, and another declarative system: make. `mmc -M <file>' discovers file's dependencies and stores these in a makefile variable $(.ms) in the file <file>.dv Given this, ltq simply builds a makefile with the enumerated dependencies and then calls the system that manages the dynamic syntax, which then writes out syntactically-enhanced modules in their canonical form (called `dopp'). Both ltq and dopp are available, along with samples as dynamic_ops.tgz.

Conclusion

This study came from my experience with the ease of use of mallable syntax in Prolog and comments in the Mercury sources about the need to add op/3 declarations as well as at least two aborted implementation attempts to do so. In the ensuing process, where I did implement this solution, quite a discussion emerged on the maillist on the estetic of allowing the user to introduce or to change syntax, and how to go about doing it properly. This implementation is one approach, and is offered to assist those who wish to add syntactic extensions to their Mercury systems.

Endnotes

1
The normal infix operators do not have this grave branding, and for good reason. Imagine writing algebraic statements, such as the following:
Aroot = sqrt((B * B - 4 * A * C) / 2 * A) - B
while shackled to the grave syntax:
Aroot `=` (sqrt(((B `*` B) `-` (4 `*` A `*` C)) `/` (2 `*` A)) `-` B)
Note the extra parentheses -- these are now necessary, as the grave syntax does not communicate operator precedence. Also note that the single-character operators are now three times their original size. Given the above, it's tempting to avoid infix syntax altogether...
(setq aroot (- (sqrt (/ (- (* b b) (* 4 a c)) (* 2 a))) b))
...but I have no desire to write out the parsed internal representation by hand (it may look like Lisp, circa 1965, because the syntax of most Lisps (with one notable exceptionis also its parsed internal representation), so the Mercury prefix code is therefore presented:
'='(Aroot, '-'(sqrt('/'('-'('*'(B, B), '*'('*'(4, A), C)), '*'(2, A))), B))
There! Isn't the canonical tree syntax so much better than the cons syntax? Drek!
2
This is not all that bad, given the documentation and the calculator2.m sample. In calc4.m we provide a straightforward example using the map type.
3
This pronouncement in no way prevented this author from submitting such a proposal to the Mercury team. Ah, the blessed ignorance of youth! All was not in vain, however: every misstep hides the seeds of greatness: one of the responses showed that samples/expand_term.m (the responder was the author of that module, in fact) provides the functionality of Prolog's term_expansion/2 predicate, which is an essential prerequesite for implementing Aspect-Oriented Programming (AOP) in predicate-logic based languages (specifically Prolog). How aspects are implemented in Mercury shall be a topic for another paper.
4
In ISO Prolog op/3 declarations have global extent.
5
[Bratko2001], § 3.3 demonstrates op/3 declared syntax with such charming statements...
:- op(300, xfx, plays).
:- op(200, xfy, and).

Term1 = jimmy plays football and squash.
Term2 = susan plays tennis and basketball and volleyball.
...but then the textbook quickly redeems itself -- it is still my preferred Prolog textbook -- with a meatier problem, which I adapt for your enjoyment:
ruth was the executive director at wncog.
sally was the executive administrative_assistant at wncog.
diane was the director of the human_resources department at wncog.
juan was the administrative_assistant of the human_resources department at wncog.
sunny was the director of the finance department at wncog.
stuart was the director of the operations department at wncog.
joe was the system_administrator of the operations department at wncog.

?- Who was the director of the What department at wncog.

Who = diane,
What = human_resources ;

Who = sunny,
What = finance ;

Who = stuart,
What = operations ;

no
I leave the op/3 declarations as a coding challenge to the enterprising reader.
6
Prolog's op/3 declarations have global extent, but I consider this a mistake in the presence of a module system -- op/3 should only affect the module in which is it declared.
7
"Worse Is Better" the catchy title of one of the most fameous apologies (after Socrates', of course), is available from several sources: http://www.jwz.org/doc/worse-is-better.html is one such.

Works Consulted

[Bratko2001]Prolog Programming for Artificial Intelligence, 3rd ed., Ivan Bratko, Addison-Wesley, Reading, Massachusetts, 2001.


(article originally posted January 3, 2006)