Skip to the content.

Overview

rrecurse ("rainbow recurse") is an SWI-Prolog module(-in-progress) that provides a couple of easy-to-use "rainbow recurse call" meta-predicates, rrcall/1 and rrcall/2. These provide developers with a simple yet robust way to add trace output to any recursive predicate. They can be used to call any goal that contains a recursive predicate. It works very similar to call(...) with the key difference being that the optional second argument is not appended to the goal arguments, but rather allows the user to specify the "head template" that should be matched as the recursive predicate to rainbow-ify. These predicates have the potential to be useful in many situations involving recursion in SWIPL code.

Features and Screenshots

[^1]: Well, it hopefully will in the future. There are currently still some issues with certain predicates that the dependencies rrecurse (and/or the dependencies' dependencies) use, such as lists:append/3. Fortunately, for most of these predicates it is not difficult at all to simply copy to a new file the source code for the predicate you'd like to rainbowify, rename the predicate, and then simply rainbowify the newly-named predicate.

FAQs

  1. Who should use this module?

    As it is not yet perfectly functional, I disrecommend using it unless you are either knowledgeable to understand how it works (or, rather, why it doesn't, lol), or are okay with sometimes inexplicable behavior.

  2. Is this module complete?

    Sadly, no. There are a couple of oddities that need to be smoothed out, mostly relating to the module's reliance on attributed variable hooks to trigger the "after_enter" print_call/4 call.

  3. What problems might the user face due to its imperfect status?

    When the rainbow-ified predicate is called in the goal from within another recursive predicate, the printing of certain subsets of recursion layers is in some cases "delayed". This is also more likely to occur when arguments are supplied to the recursive predicate that are nonvar and nonground --that is, they are bound to a term that contains (unbound) variables.

    • ted_shortest_length/3 (which is not recursive) calls ted_shortest_length/4, which is recursive.
    • Within the recursive predicate ted_shortest_length/4, another recursive predicate, ted_path/4, is called (via calls to non-recursive ted_path/3, which calls ted_path/4).

    image

    image

    Using rrcall/1 or rrcall/2 currently sometimes makes otherwise deterministic goals non-deterministic. There is likely no surefire way to avoid this unfortunate side-effect until I manage to eliminate the addition of superfluous choice-points.

    The first few lines are not included in this screenshot. Goal was lists:subtract([a,1,b,2,c,3],[1,2,3],Sub).

    image

  4. Will more features be added in the future?

    Yes! I have a number of ideas for additional customization capability, potentially by defining hooks. I'd also like to find a way to (optionally?) provide more visual feedback for the backtracking, most likely by making use of the undo/1 built-in. But first, I really need to fix the issue I mentioned in the answer to Question 3.

Invocation Syntax

Note on the Template argument

From the documentation:

When Template is not specified, Goal is expected to be a single, standalone invocation of a recursive predicate.

When Template is provided, Goal can be any syntactically valid callable term. Occurrences of Template within Goal will be wrapped with rrcall(...). The instantiation of the argument(s) of Template is taken into consideration when making these replacements, but it is disregarded when temporarily wrapping the definition of the predicate that Template refers to.

In other words, given a Template with name Name and of arity Arity:

Detailed Documentation

I attempted to thoroughly document this module using structured comments. You can read these structured comments in the source file.

If you'd like to read the contents of these comments presented in a more eyeball-friendly fashion, simply follow the following procedure:

  1. At toplevel, simply execute the query goal use_module(library(pldoc)), use_module(library(doc_files)) before loading or importing the rrecurse module.
  2. Once rrecurse has been loaded/imported via use_module/1, etc, then call doc_save(rrecurse, [format(html)]).
  3. There shall appear in the same directory as the rrecurse.pro file a few PNG image files, a CSS file, and an HTML file, all of which were generated by Prolog during the execution of doc_save/2. Open the HTML file in the web browser of your choice and prepare to be delighted by my lovely documentation. Below is a screenshot of the generated documentation files as of 03/07/2022: image