Add working examples to PLDoc

Alright, I put together a quick proof-of-concept and I’ve made it public at GitLab (MIT License) so we
can work on it together. I just spotted a bug, but I’ve gotta get on with my actual work now so will have to fix it later:

```example
   ?- my_sort([3, 1, 2], X), % ← currently needs a comma but should be full-stop
   X = [1, 2, 3].
```

It’ll just be a quick improvement to the DCG to fix. There’s work to be done on this, but the proof-of-concept is here so we can discuss what’s good, bad and what we want to do with it.

3 Likes

Paul,

I split the topic out. Feel free to change the title.

Others,

Note: This topic started from a question by joeblog

1 Like

These are just comments, feel free to disregard.

  1. While the Python document system used
    ```
    as a tag for locating source code in the documentation, I think it would be better to make use of the current PlDoc system of @Tag for locating examples. Since no existing tag works for the needs of example code, new tag(s) should be created.

The current PlDoc tags are only for one line or word, but source code needs multiple lines, need bookend type tags, e.g. @example_start, and @example_end

% ```example
% ?- my_sort([4,3,1,2], Sorted),
% Sorted = [1, 2, 3, 4].
% ```

would be added to the Prolog documentation as

% @example_start
% ?- my_sort([4,3,1,2], Sorted),
% Sorted = [1, 2, 3, 4].
% @example_end
  1. Since part of the need is to validate the example code, it should use the existing SWI-Prolog unit test.
% @example_start
% ?- my_sort([4,3,1,2], Sorted),
% Sorted = [1, 2, 3, 4].
% @example_end

should be

% @example_start
% :- begin_tests(my_sort).
%
% test_001_test_case([4,3,1,2],[1,2,3,4]).
% test(001, [forall(test_001_test_case(Input,Expected_result))] ) :-
%    my_sort(Input,Result),  
%    assertion( Result == Expected_result).
%
% :- end_tests(my_sort).
% @example_end
  1. While the industry standard is to write one test with a verbose description per test, IMHO what works better is seeing all of the test cases lined up for quick comparison.
% @example_start
% :- begin_tests(my_sort).
%
% test_001_test_case([],[]).
% test_001_test_case([1],[1]).
% test_001_test_case([1,2],[1,2]).
% test_001_test_case([2,1],[1,2]).
% test_001_test_case([1,2,3],[1,2,3]).
% test_001_test_case([1,3,2],[1,2,3]).
% test_001_test_case([2,3,1],[1,2,3]).
% test_001_test_case([2,1,3],[1,2,3]).
% test_001_test_case([3,1,2],[1,2,3]).
% test_001_test_case([3,2,1],[1,2,3]).
%
% test_001_test_case([a],[a]).
% test_001_test_case([a,b],[a,b]).
% test_001_test_case([b,a],[a,b]).
% test_001_test_case([a,b,c],[a,b,c]).
% test_001_test_case([a,c,b],[a,b,c]).
% test_001_test_case([b,c,a],[a,b,c]).
% test_001_test_case([b,a,c],[a,b,c]).
% test_001_test_case([c,a,b],[a,b,c]).
% test_001_test_case([c,b,a],[a,b,c]).
%
% test(001, [forall(test_001_test_case(Input,Expected_result))] ) :-
% my_sort(Input,Result),
% assertion( Result == Expected_result).
%
% :- end_tests(my_sort).
% @example_end
  1. Now that there are too many tests and another purpose of this is to help others learn, should some of the tests be shown and some of the test run without being shown? Should a user be able to choose which test to run? I think the idea of converting the example code into a SWISH console would work better.

  2. If the concept of grabbing the code from HTML and putting into a SWISH console is used, then maybe extending PlDoc with tags is the wrong way to code and instead modify PlDoc to create HTML tags friendly to the lpn-swish-proxy

I don’t really see what is wrong with ```example or ```test for that matter. PlDoc support it and it suits the job. @-tags serve a different purpose.

I’m also not a big fan of using PlUnit syntax in such tests/examples. If you want to use PlUnit, do so in the normal code. That has all the facilities to include or exclude the tests as well as for running and debugging them. Possibly we can think of a way to link tests to the predicate they test. At the moment this link is unclear. One simple idea would be to use the predicate indicator as name for the test set, e.g.,

:- begin_tests(mypred/1).
...

At least in theory the documentation system could link to these examples or even include them in the docs (but I think that is mostly ugly).

If an example/test appears in PlDoc, I’d say

  • Normal clauses form the program (that is loaded after :- use_module for the module documented.
  • ?- Goal. is a test goal
  • The subsequent term is the answer.

As SWI-Prolog answers are always a valid Prolog term, this is easy. Of course, the program can be missing. There can also be multiple goals/queries. The above conventions easily feed an LPN like embedding of SWISH.

I would propose this for reasons already mentioned not to be part of PlUnit, but simply a part of PlDoc. When processing the source, PlDoc will (optionally), run the associated tests/examples.

1 Like

I did a quick look of the HTML of a PlDoc page, Predicates that operate on strings and find that the code can be identified easily with

<pre class="code">

however just dumping that into SWISH is not as straight forward as it would seem. Does the code get loaded into a Program or Notebook? My guess would be Notebook.

Then if the code is placed in a notebook, is the code a Program cell or a Query cell? That is easy for a human to figure out, so instead of just using the

```example
and
```

as Paul suggested, we have a few more such as

```example_program

and

```example_query

Also since SWISH can support Markdown and HTML cells, it would be nice to be able to have the PlDoc make use of this, but hide those items in the display of the SWI-Prolog documentation, but be present in the SWISH console.

Note: The above code would scan the HTML generated by PlDoc and not scan the source file used by PlDoc to make the enhanced HTML page. But the changes would be made to the files used as input by PlDoc.