This section describes how to include editable and runnable code blocks in LPdoc documents, to create interactive Active Logic Documents (ALDs). These code blocks can either run embedded in the document or be loaded into a playground,
A runnable code block is a special documentation code block where the language is marked as ciao_runnable, as follows:
```ciao_runnable ```These code fragments are automatically rendered as editable cells that can be run, either in place, embedded in the document, for formats/backends that support this (typically html), or loaded into a separate playground. Additional commands can be included within the code which allow marking code areas specially to provide different behaviors and functionality. Below, we provide an overview of the available commands along with a brief explanation for each:
To reload or reset the code in this cell, simply click on the green tick mark located in the top left corner of the pane. This functionality enables you to reload the code at any time.
Example:
```ciao_runnable :- module(_, [qsort/2], []). %! \begin{focus} qsort(Data, Out) :- qsort_(Data, Out, []). qsort_([], R, R). qsort_([X|L], R, R0) :- partition(L, X, L1, L2), qsort_(L2, R1, R0), qsort_(L1, R, [X|R1]). %! \end{focus} partition([],_,[],[]). partition([X|L],Y,[X|L1],L2) :- X =< Y, !, partition(L,Y,L1,L2). partition([X|L],Y,L1,[X|L2]) :- partition(L,Y,L1,L2). ```Which produces the following cell:
:- module(_, [qsort/2], []). %! \begin{focus} qsort(Data, Out) :- qsort_(Data, Out, []). qsort_([], R, R). qsort_([X|L], R, R0) :- partition(L, X, L1, L2), qsort_(L2, R1, R0), qsort_(L1, R, [X|R1]). %! \end{focus} partition([],_,[],[]). partition([X|L],Y,[X|L1],L2) :- X =< Y, !, partition(L,Y,L1,L2). partition([X|L],Y,L1,[X|L2]) :- partition(L,Y,L1,L2).
```ciao_runnable ?- qsort([1,5,4,2,3],X). ```Which produces:
?- qsort([1,5,4,2,3],X).There is essentially one toplevel per page; all programs in a given ALD page are loaded into this toplevel and all queries in the page are executed in that toplevel, against all the code (possibly separate modules) that has been loaded into the toplevel at that point.
Segments enclosed within hint directives function similarly to focus segments, and are used typically to offer the hints or instructions mentioned above. However, if the reader requests to see the solution, then, the hint segment will be replaced with the corresponding solution, marked by solution directives.
Example:
```ciao_runnable %! \begin{hint} Proposed exercise %! \end{hint} (Optional) Unit tests %! \begin{solution} Solution %! \end{solution} ```
The ciao_runnable language tag also allows the following additional annotations to interact with the browser using Javascript and/or Prolog code running through WebAssembly.
```ciao_runnable %! \begin{jseval} async() => { <<JSCODE>> } %! \end{jseval} ```Example (when included, the HTML document will turn upside-down):
```ciao_runnable %! \begin{jseval} async() => { document.body.style.transform = "rotate(180deg);" } %! \end{jseval} ```
```ciao_runnable %! \begin{dynpreview} { render_pred: 'bundles_dyn:render', state_hash: true, depends: ['ciaowasm', 'website', 'lpdoc'], on_init: ['use_module(catalog_ui(bundles_dyn))'] } %! \end{dynpreview} ```The render_pred field specifies the predicate that will generate the HTML contents (as a string, e.g., using pillow). The state_hash field specifies if the URL hash is passed as parameter (e.g., to maintain a state). The rest of fields indicate the bundle dependencies and initialization goals.
The following example shows a programming task with some initial hints, a valid solution (there may be others), and some tests to validate the user code:
```ciao_runnable :- module(_, _, [assertions]). :- test factorial(A, B) : (A = 0) => (B = 1) + (not_fails, is_det). :- test factorial(A, B) : (A = 1) => (B = 1) + (not_fails, is_det). :- test factorial(A, B) : (A = 2) => (B = 2) + (not_fails, is_det). :- test factorial(A, B) : (A = 3) => (B = 6) + (not_fails, is_det). :- test factorial(A, B) : (A = 4) => (B = 24) + (not_fails, is_det). :- test factorial(A, B) : (A = 5) => (B = 120) + (not_fails, is_det). :- test factorial(A, B) : (A = 0, B = 0) + (fails, is_det). :- test factorial(A, B) : (A = 5, B = 125) + (fails, is_det). :- test factorial(A, B) : (A = -1) + (fails, is_det). %! \begin{hint} % TASK 1 - Rewrite with Prolog arithmetic factorial(0,s(0)). % TODO: Replace s(0) by 1 factorial(M,F) :- % TODO: Make sure that M > 0 M = s(N), % TODO: Compute N from M using is/2 (note that N is factorial(N,F1), % unbound, so you need to compute N from M!) times(M,F1,F). % TODO: Replace times/3 by a call to is/2 (using *) % When you are done, press the circle ("Run tests") or the arrow % ("Load into playground"). %! \end{hint} %! \begin{solution} factorial(0,1). factorial(N,F) :- N > 0, N1 is N-1, factorial(N1,F1), F is F1*N. %! \end{solution} ```This produces the following cell:
:- module(_, _, [assertions]). :- test factorial(A, B) : (A = 0) => (B = 1) + (not_fails, is_det). :- test factorial(A, B) : (A = 1) => (B = 1) + (not_fails, is_det). :- test factorial(A, B) : (A = 2) => (B = 2) + (not_fails, is_det). :- test factorial(A, B) : (A = 3) => (B = 6) + (not_fails, is_det). :- test factorial(A, B) : (A = 4) => (B = 24) + (not_fails, is_det). :- test factorial(A, B) : (A = 5) => (B = 120) + (not_fails, is_det). :- test factorial(A, B) : (A = 0, B = 0) + (fails, is_det). :- test factorial(A, B) : (A = 5, B = 125) + (fails, is_det). :- test factorial(A, B) : (A = -1) + (fails, is_det). %! \begin{hint} % TASK 1 - Rewrite with Prolog arithmetic factorial(0,s(0)). % TODO: Replace s(0) by 1 factorial(M,F) :- % TODO: Make sure that M > 0 M = s(N), % TODO: Compute N from M using is/2 (note that N is factorial(N,F1), % unbound, so you need to compute N from M!) times(M,F1,F). % TODO: Replace times/3 by a call to is/2 (using *) % When you are done, press the circle ("Run tests") or the arrow % ("Load into playground"). %! \end{hint} %! \begin{solution} factorial(0,1). factorial(N,F) :- N > 0, N1 is N-1, factorial(N1,F1), F is F1*N. %! \end{solution}The examples shows how tests can be included, hints and solutions provided, etc.
The following example illustrates the use of @begin{focus} and @end{focus} directives to specify that only some parts of the code be shown:
```ciao_runnable :- module(_, _, [assertions,sr/bfall]). %! \begin{focus} factorial(0,s(0)). factorial(s(N),F) :- factorial(N,F1), times(s(N),F1,F). %! \end{focus} nat_num(0). nat_num(s(X)) :- nat_num(X). times(0,Y,0) :- nat_num(Y). times(s(X),Y,Z) :- plus(W,Y,Z), times(X,Y,W). plus(0,Y,Y) :- nat_num(Y). plus(s(X),Y,s(Z)) :- plus(X,Y,Z). ```results in:
:- module(_, _, [assertions,sr/bfall]). %! \begin{focus} factorial(0,s(0)). factorial(s(N),F) :- factorial(N,F1), times(s(N),F1,F). %! \end{focus} nat_num(0). nat_num(s(X)) :- nat_num(X). times(0,Y,0) :- nat_num(Y). times(s(X),Y,Z) :- plus(W,Y,Z), times(X,Y,W). plus(0,Y,Y) :- nat_num(Y). plus(s(X),Y,s(Z)) :- plus(X,Y,Z).Programs can be modules or 'user' (i.e., non-modular) code. The focus facility can be used as shown above to select whether boilerplate lines (such as, e.g., module declarations, imports, auxiliary code, etc.) are shown in the output or not.
Finally, the following are examples of runnable and editable queries:
```ciao_runnable ?- factorial(X,s(s(s(s(s(s(0))))))). ```resulting in:
?- factorial(X,s(s(s(s(s(s(0))))))).As mentioned before, there is essentially one toplevel per page; all programs in a given ALD are loaded into this toplevel and all queries in the page are executed in that toplevel, against all the code (possibly separate modules) that has been loaded into the toplevel at that point.
You can consult the subparts below to view a complete example, including the full source code and generated output.