SWISH Code and Query Blocks

At the core of our sphinx-prolog extension is the swish module; it enables creation of interactive SWI Prolog code boxes by abstracting their setup complexities away from the content creator. Interactivity is achieved by dynamically embedding SWISH windows that bring an online, collaborative Prolog execution environment. The sphinx_prolog.swish Sphinx extension module implements two types of display boxes – swish and swish-query – and one inline code listing introduced with swish-query. The swish command is responsible for creating interactive SWISH Prolog code blocks. Both swish-query instructions, on the other hand, embed plain-looking code listings designated for Prolog queries, which can be exported to or imported by SWISH code blocks.

Setup

To enable the sphinx_prolog.swish extension module in your Jupyter Book, include its name in your _config.yml file under the sphinx.extra_extensions key.

sphinx:
  extra_extensions:
    # Load sphinx-prolog <https://github.com/simply-logical/sphinx-prolog>
    - sphinx_prolog.swish

See also

See the Usage section of the sphinx-prolog Extension page for more details.

Configuration

The sphinx_prolog.swish extension can optionally be configured to better accommodate a number of use cases. Similar to Exercise and Solution Blocks, the content of SWISH boxes can be loaded from external (Prolog code) files. The SWISH server – https://swish.swi-prolog.org/ by default – used to execute the interactive code boxes may also be specified by the user. If the Prolog queries for some SWISH code boxes are provided verbatim (more on that in the Explicit Queries section), they can be hidden from the reader to improve readability. Lastly, the default procedure for creating interactive SWISH blocks limits their size to 2,048 characters – the maximum length of a URL – overcoming which requires manual configuration.

Setting up External Files

To enable populating interactive SWISH code boxes from external Prolog code files, their location (path to a directory) must be specified in the sphinx-prolog extension settings via the sp_code_directory configuration parameter.

sphinx:
  config:
    # Configure sphinx-prolog <https://github.com/simply-logical/sphinx-prolog>
    sp_code_directory: src/code/

It is required when loading the content of SWISH boxes from files, or when implicitly prepending or appending Prolog code to SWISH boxes using the source-text-start and source-text-end parameters (see the Advanced Code Boxes section below for more details). Additionally, the Prolog files must have the .pl extension.

See also

Loading Prolog files into interactive SWISH code boxes is explained in the Code Boxes section below.

Specifying SWISH Server

The default SWISH execution server is https://swish.swi-prolog.org/. It can be changed by specifying the sp_swish_url configuration parameter.

sphinx:
  config:
    # Configure sphinx-prolog <https://github.com/simply-logical/sphinx-prolog>
    sp_swish_url: https://swish.simply-logical.space/

You may consider hosting your own SWISH server if you would like to install custom SWI Prolog libraries and other code dependencies needed for your use case.

Tip

In addition to Prolog, the sphinx-prolog package can be used to build documents with interactive cplint code blocks. More information can be found at http://cplint-template.simply-logical.space/.

Note

Historically, hosting your page under a domain different than the one of the specified SWISH server prevented the code blocks from loading due to cross-origin requests being blocked by default. One way to resolve the problem with SWISH code boxes not working was to enable third-party cookies in your web browser. This is not the case any more, so you should be fine connecting to https://swish.swi-prolog.org/. For this reason, we have https://swish.simply-logical.space/ linked to https://swish.swi-prolog.org/ – both URL access the same server, which is kindly hosted by the awesome SWI Prolog team.

Hiding SWISH Queries

One way to pre-populate Prolog queries into your interactive SWISH boxes is to include a specially formatted examples section in your code, e.g.,

...

/** <examples>
?- my_query(a,X).
?- my_query(b,X).
*/

(more on that in the Explicit Queries section below). Displaying these examples blocks in SWISH code boxes may be distracting, and you may wish to hide them by default. This can be achieved by setting the optional sp_swish_hide_examples configuration parameter to true (it defaults to false).

sphinx:
  config:
    # Configure sphinx-prolog <https://github.com/simply-logical/sphinx-prolog>
    sp_swish_hide_examples: true

While this sphinx-prolog extension setting toggles global visibility of the examples blocks, you can override this behaviour for individual SWISH boxes with their hide-examples parameter – see the Hiding Queries section below for more details.

Loading Long SWISH Scripts

Your Prolog code and queries are transferred to the interactive SWISH boxes within a URL request, e.g., https://swish.swi-prolog.org/?code=exists_extension(sphinx,prolog).&q=exists_extension(sphinx,A).. Since URL requests are limited to 2,048 characters, longer box content cannot be transferred with this approach. To support large Prolog scripts, a different SWISH URL request format can be used: https://swish.swi-prolog.org/?code=https://book-template.simply-logical.space/_sources/prolog_build_files/long-complex-merged.pl&q=exists_extension(jupyter_book,A)., where the code section specifies the web address of a Prolog code file. Such code files can be generated automatically by this extension and uploaded alongside your book to support long Prolog scripts. The entire procedure is executed under the hood and such code boxes are no different to standard SWISH boxes, however this functionality must be enabled for each individual code block via its build-file parameter – see the Long Boxes section below for more details.

While such boxes will function as expected when the book is published online, they will not be able to load the underlying Prolog script when the book is built locally during development. Since the Prolog file used by such a SWISH block is created by the Jupyter Book build process, it will not be accessible to the code box until the book is uploaded online. To enable this functionality, the sphinx-prolog extension must know the base URL under which your book will be published. This information can be supplied via the sp_swish_book_url configuration setting.

sphinx:
  config:
    # Configure sphinx-prolog <https://github.com/simply-logical/sphinx-prolog>
    sp_swish_book_url: https://book-template.simply-logical.space/

It is used to compose links to Prolog code files that need to be accessed by file-based SWISH boxes.

The Prolog files underlying such SWISH code blocks are temporarily stored in the src/code/temp/ directory (relative to the root of your book) before being copied over to the _sources/prolog_build_files/ folder located in the book build directory _build/html/. To avoid unnecessarily storing these files in your git repository, you may want to add the src/code/temp/ path to your .gitignore. Additionally, this temporary storage location should be excluded from the Jupyter Book build path by adding it to the exclude_patterns list in your _config.yml configuration file.

exclude_patterns:
  - src/code/temp

This will help to avoid build issues down the line.

Warning

File-based SWISH code boxes – enabled by the build-file parameter individually for each SWISH block – will not work for local builds of your book since their underlying Prolog files cannot be accessed by SWISH until you upload them online alongside your book.

Usage

Display SWISH code and query blocks are included using an admonition-like syntax, with their content provided explicitly in the definition statement. To build either of those two boxes use a triple-backtick fence respectively with swish or swish-query keyword wrapped in curly braces followed by a user-defined tag, i.e., ```{swish} swish:my-tag and ```{swish-query} swishq:my-tag. An inline SWISH query, on the other hand, is introduced using reference-like syntax, with its tag inserted in angle bracket after the query text, i.e., {swish-query}`?- query(Q). <swishq:my-tag>`.

The Prolog content included with these statements can be referenced. Since Prolog elements are unnumbered, they can only be hyper-linked with a pre-defined name using the {ref}`my-tag` syntax. In particular:

  • referencing a SWISH code box – {ref}`swish:my-tag` – produces a “SWISH code box” hyper-link;

  • referencing a SWISH query box (display) – {ref}`swishq:my-display-tag` – produces a “SWISH query box” hyper-link; and

  • referencing a SWISH query listing (inline) – {ref}`swishq:my-inline-tag` – produces a “SWISH query listing” hyper-link.

Keep in mind that the text of these hyper-links can be changed manually using the {ref}`Custom Text <my-tag>` syntax.

See also

See the Referencing section of the Exercise and Solution Blocks page and the Usage section of the sphinx-prolog Extension page for more details about referencing.

Code Boxes

Interactive swish code boxes can either list their Prolog script explicitly or load it from an external programme file. Each swish block must be tagged with a label prefixed with swish:.

Tip

To improve readability of interactive SWISH code boxes and the markdown syntax responsible for their creation, we use tabs instead of side-by-side display panels utilised thus far. To access the source of each SWISH box presented on this page, click the Syntax tab above the code block.

Verbatim Code

The easiest way to construct an interactive SWISH code box is to include your Prolog script within the box.

exists_extension(sphinx, jupyter_book).
exists_extension(sphinx, prolog).

/** <examples>
?- exists_extension(sphinx, A).
*/
```{swish} swish:simple-example
exists_extension(sphinx, jupyter_book).
exists_extension(sphinx, prolog).

/** <examples>
?- exists_extension(sphinx, A).
*/
```

Code File

If you prefer to store your Prolog scripts in external programme files, you can load them into swish code boxes by placing them in a dedicated directory and setting up the sphinx-prolog extension appropriately. (See the Setting up External Files section above for more details and setup instructions.) The Prolog files must have .pl extension; they are loaded into swish code boxes by formatting the tags of such boxes based on the names of these files. To this end, prepend the swish: prefix to and discard the .pl extension from the selected Prolog file name. For example, the file named prolog_file.pl stored in src/code/ can be loaded into a selected SWISH code box with the swish:prolog_file tag.

% Source path: src/code/prolog_file.pl

exists_extension(sphinx, jupyter_book).
exists_extension(sphinx, prolog).

/** <examples>
?- exists_extension(sphinx, A).
*/
```{swish} swish:prolog_file
```

As with exercise and solution blocks, the content of a SWISH box loaded from an external file can be overwritten by inserting a Prolog script directly in the box definition.

exists_extension(jupyter_book, prolog).
exists_extension(jupyter_book, markdown).

/** <examples>
?- exists_extension(jupyter_book, A).
*/
```{swish} swish:prolog_file
exists_extension(jupyter_book, prolog).
exists_extension(jupyter_book, markdown).

/** <examples>
?- exists_extension(jupyter_book, A).
*/
```

Note

If you look at the markdown source of this document, you will notice that the syntax of the SWISH code box placed directly above uses a different tag (swish:prolog_file) than the actual definition of this block (swish:prolog_file_copy). Since two SWISH code boxes cannot be built from the same source file (due to a label conflict), we duplicated the prolog_file.pl Prolog source file with the prolog_file_copy.pl name.

Query Boxes

SWISH code boxes can be populated with Prolog queries in a number of ways. Queries can be manually inserted into SWISH code boxes or automatically imported from selected SWISH query elements. The latter approach requires the user to embed and tag this type of content in the markdown source of a page. There are two types of such boxes: inline and display, each one with a dedicated syntax based on the swish-query keyword. Each SWISH query element must be tagged with a label prefixed with swishq:. Both inline and display query elements can contain multiple Prolog queries, each one starting with ?- and finished with .. Inline query elements containing a single Prolog query may omit the opening ?- and closing . symbols.

Inline

Inline SWISH queries appear as code snippets displayed in typewriter font without syntax highlighting.

Outcome

A possible SWISH query embedded inline is ?- exists_extension(jupyter_book, Inline)..

Syntax

A possible SWISH query embedded inline is {swish-query}`?- exists_extension(jupyter_book, Inline). <swishq:inline>`.

Display

Display SWISH queries appear as block code listings formatted with the Prolog syntax highlighting.

Outcome

A SWISH query can also be displayed as a block:

?- exists_extension(jupyter_book,
                    Display).

Syntax

A SWISH query can also be displayed as a block:

```{swish-query} swishq:display
?- exists_extension(jupyter_book,
                    Display).
```

Importing tagged Prolog queries into SWISH code boxes is mostly initiated by each code block individually. However, in some cases it may be more convenient to reverse this process and export a SWISH query into selected code blocks. The optional source-id parameter is used to this end – it takes a space-separated list of tags of the target code boxes. This functionality is only available for display swish-query blocks. Multiple query boxes can be exported to a single SWISH code block.

Outcome

A display SWISH query can be exported into selected code blocks.

?- exists_extension(jupyter_book,
                    Injected).

Syntax

A display SWISH query can be exported into selected code blocks.

```{swish-query} swishq:display-in
---
source-id: swish:in-1 swish:in-2
---
?- exists_extension(jupyter_book,
                    Injected).
```

This query is exported to the two SWISH code boxes displayed below. Note that the export procedure overrides the queries listed in SWISH code boxes explicitly via the /** <examples> ... */ syntax. Therefore, the second code box will disregard the verbatim exists_extension(sphinx, Q). query and only present the user with the exported exists_extension(jupyter_book, Injected) query. Queries can only be exported to SWISH code boxes placed on the same page, i.e., in the same markdown document.

exists_extension(jupyter_book, prolog).
exists_extension(jupyter_book, markdown).
exists_extension(jupyter_book, prolog).
exists_extension(jupyter_book, markdown).

/** <examples>
?- exists_extension(jupyter_book, Q).
*/
```{swish} swish:in-1
exists_extension(jupyter_book, prolog).
exists_extension(jupyter_book, markdown).
```

```{swish} swish:in-2
exists_extension(jupyter_book, prolog).
exists_extension(jupyter_book, markdown).

/** <examples>
?- exists_extension(jupyter_book, Q).
*/
```

Implicit Queries

The more natural approach to including Prolog queries in your interactive SWISH code blocks is to either assign them directly to a box or import them from query blocks.

Hard-coding Queries

Prolog queries can be included in a SWISH code box using the query-text parameter. It simply lists the queries that are to be available in a code block without revealing them to the user prior to running the box.

query-text: |
  ?- exists_extension(jupyter_book, HQ_1).
  ?- exists_extension(jupyter_book, HQ_2).

Each query must be prefixed with ?- and finished with ., with multiple queries separated by a white space. The | notation is a syntactic sugar that allows to split a parameter string into multiple lines.

exists_extension(jupyter_book, prolog).
exists_extension(jupyter_book, markdown).
```{swish} swish:hq
---
query-text: |
  ?- exists_extension(jupyter_book, HQ_1).
  ?- exists_extension(jupyter_book, HQ_2).
---
exists_extension(jupyter_book, prolog).
exists_extension(jupyter_book, markdown).
```

Including Prolog queries via the query-text SWISH box parameter is compatible with the query import facilitated via the query-id parameter described below and the source-id export parameter of SWISH query boxes. However, this procedure overrides the queries provided explicitly in SWISH code boxes with the /** <examples> ... */ syntax.

Importing Queries

Another approach to populating interactive SWISH code blocks with Prolog queries is importing them from SWISH query boxes. It is achieved via the query-id parameter, which lists the tags of query boxes that ought to be imported.

query-id: swishq:inline swishq:display swishq:display-in

Queries can only be imported from SWISH query boxes placed on the same page, i.e., in the same markdown document.

exists_extension(jupyter_book, prolog).
exists_extension(jupyter_book, markdown).
```{swish} swish:iq
---
query-id: swishq:inline swishq:display swishq:display-in
---
exists_extension(jupyter_book, prolog).
exists_extension(jupyter_book, markdown).
```

Importing Prolog queries via the query-id SWISH box parameter is compatible with the queries defined via the query-text parameter described above and the source-id export parameter of SWISH query boxes. However, this procedure overrides the queries provided explicitly in SWISH code boxes with the /** <examples> ... */ syntax.

Explicit Queries

All of the Prolog query input methods described so far operate at the level of defining SWISH code boxes in your markdown source. Moreover, such queries are invisible to the user until the code block is launched. This may be sub-optimal when you want to display the queries directly in the embedded SWISH code boxes or if you want to distribute them alongside your Prolog script – either verbatim in the code block (markdown source) or within a standalone Prolog file that is being imported. In these cases, you can insert your queries explicitly in the Prolog script within a specially formatted comment block: /** <examples> ... */ (see the official SWISH help page for more details).

...

/** <examples>
?- exists_extension(jupyter_book, HQ_1).
?- exists_extension(jupyter_book, HQ_2).
*/

Queries distributed in this way can be easily overridden with the SWISH code blocks’ query-text and query-id parameters and the source-id parameter of SWISH query boxes.

Tip

A SWISH code block can be supplied with Prolog queries either explicitly by including the dedicated /** <examples> ... */ comment bloc or implicitly via the box parameters. The latter method includes importing queries from SWISH query blocks using the query-id parameter or inputting them directly via the query-text parameter. Alternatively, SWISH queries can be exported from display query boxes to selected SWISH code blocks via the source-id parameter of query boxes. The three implicit methods – query-id, query-text and source-id – are cross-compatible, and the presence of any single one of them overrides the queries provided explicitly via the /** <examples> ... */ comment bloc. Note that queries can only be imported to SWISH code blocks and exported from SWISH query boxes if these elements are placed on the same page, i.e., within a single markdown document.

Hiding Queries

You may want to have the benefits of storing your queries in Prolog files or verbatim scripts, e.g., for completeness, but prefer to hide them from the user. To this end, you can toggle visibility of the /** <examples> ... */ comment blocks either globally for the entire book or locally for individual code boxes. The former is explained in the Hiding SWISH Queries section above. The global behaviour can be overridden locally using the hide-examples parameter of SWISH code boxes.

hide-examples: true

This switch works for verbatim and file-based code blocks.

You will only see the /** <examples> ... */ portion of this code box in the programme section of the SWISH window after launching it.

exists_extension(jupyter_book, prolog).
exists_extension(jupyter_book, markdown).
/** <examples> ?- exists_extension(jupyter_book, HiddenQuery). */

You will only see the /** <examples> ... */ portion of this code box in the programme section of the SWISH window after launching it.

```{swish} swish:hide
---
hide-examples: true
---
exists_extension(jupyter_book, prolog).
exists_extension(jupyter_book, markdown).

/** <examples>
?- exists_extension(jupyter_book, HiddenQuery).
*/
```

Advanced Code Boxes

In certain cases, a SWISH code box may depend on a Prolog programme that has been split into multiple code blocks scattered throughout a page. To avoid manually repeating these pieces of code in multiple boxes, we allow each SWISH code block to inherit content from multiple other boxes. Additionally, we facilitate injecting external Prolog code into a SWISH code block such that it is only visible upon launching this box. Both these operations can be applied to a single code block simultaneously. When a piece of code is inherited or injected, it is stripped of all its /** <examples> ... */ blocks to avoid accumulating unwanted queries.

Warning

When inheriting code from SWISH code blocks, all of the boxes involved in this procedure must reside on the same page, i.e., be placed in a single markdown file. Code injection is not affected by this limitation since it is based on external Prolog files.

Code Inheritance

The inheritance is implicit and the inherited code gets injected only when a box is launched, therefore not affecting its appearance. Multiple code blocks can be inherited using the inherit-id parameter, which takes a space-separated list of code box tags.

inherit-id: swish:iq swish:hide
exists_extension(jupyter_book, prolog).
exists_extension(jupyter_book, markdown).

/** <examples>
?- exists_extension(jupyter_book, InheritingCodeBox).
*/
```{swish} swish:inherit
---
inherit-id: swish:iq swish:hide
---
exists_extension(jupyter_book, prolog).
exists_extension(jupyter_book, markdown).

/** <examples>
?- exists_extension(jupyter_book, InheritingCodeBox).
*/
```

Code Injection

In addition to inheriting code from existing SWISH code block, code from external Prolog files can be prepended or appended to a code box. These Prolog programme files must be placed in the designated code directory – see the Setting up External Files section above for more details. The code injection is achieved with the source-text-start and source-text-end parameters of SWISH code boxes. Each one takes a single Prolog code file name without the .pl extension, for example

source-text-start: prepend_code
source-text-end: append_code

uses the two files – prepend_code.pl and append_code.pl – located in the src/code/ directory. Note that in this way you can include a Prolog code file that is already being used as the main source of an existing SWISH code block without any conflicts. For example, you can prepend or append the prolog_file.pl file despite it being used as the source of this SWISH code box. Similar to inheritance, code injection is implicit, hence the prepended and appended code is only visible after launching a code box.

Note that the /** <examples> ... */ block is purged from the append_code.pl code file when it is being injected.

exists_extension(jupyter_book, prolog).
exists_extension(jupyter_book, markdown).

/** <examples>
?- exists_extension(jupyter_book, ExplicitQuery).
*/

Note that the /** <examples> ... */ block is purged from the append_code.pl code file when it is being injected.

```{swish} swish:inject
---
source-text-start: prepend_code
source-text-end: append_code
---
exists_extension(jupyter_book, prolog).
exists_extension(jupyter_book, markdown).

/** <examples>
?- exists_extension(jupyter_book, ExplicitQuery).
*/
```

Long Boxes

The default technology used to construct SWISH code boxes limits their size – see the Loading Long SWISH Scripts section for more details about this issue and the sphinx-prolog configuration setup necessary to overcome it. Note that the main, imported and injected code as well as the implicit, inherited and exported queries all count towards the code box size. The SWISH code block embedded below is loaded from the long_programme.pl code file whose length exceeds the size limit, therefore the box fails to materialise when attempting to launch it.

% Source path: src/code/long_programme.pl
% Simply Logical, Chapter 1

connected(bond_street, oxford_circus, central).
connected(oxford_circus, tottenham_court_road, central).
connected(bond_street, green_park, jubilee).
connected(green_park, charing_cross, jubilee).
connected(green_park, piccadilly_circus, piccadilly).
connected(piccadilly_circus, leicester_square, piccadilly).
connected(green_park, oxford_circus, victoria).
connected(oxford_circus, piccadilly_circus, bakerloo).
connected(piccadilly_circus, charing_cross, bakerloo).
connected(tottenham_court_road, leicester_square, northern).
connected(leicester_square, charing_cross, northern).

% copy
connected(bond_street, oxford_circus, central).
connected(oxford_circus, tottenham_court_road, central).
connected(bond_street, green_park, jubilee).
connected(green_park, charing_cross, jubilee).
connected(green_park, piccadilly_circus, piccadilly).
connected(piccadilly_circus, leicester_square, piccadilly).
connected(green_park, oxford_circus, victoria).
connected(oxford_circus, piccadilly_circus, bakerloo).
connected(piccadilly_circus, charing_cross, bakerloo).
connected(tottenham_court_road, leicester_square, northern).
connected(leicester_square, charing_cross, northern).

/*
nearby(bond_street, oxford_circus).
nearby(oxford_circus, tottenham_court_road).
nearby(bond_street, tottenham_court_road).
nearby(bond_street, green_park).
nearby(green_park, charing_cross).
nearby(bond_street, charing_cross).
nearby(green_park, piccadilly_circus).
nearby(piccadilly_circus, leicester_square).
nearby(green_park, leicester_square).
nearby(green_park, oxford_circus).
nearby(oxford_circus, piccadilly_circus).
nearby(piccadilly_circus, charing_cross).
nearby(oxford_circus, charing_cross).
nearby(tottenham_court_road, leicester_square).
nearby(leicester_square, charing_cross).
nearby(tottenham_court_road, charing_cross).
*/

/*
% copy
nearby(bond_street, oxford_circus).
nearby(oxford_circus, tottenham_court_road).
nearby(bond_street, tottenham_court_road).
nearby(bond_street, green_park).
nearby(green_park, charing_cross).
nearby(bond_street, charing_cross).
nearby(green_park, piccadilly_circus).
nearby(piccadilly_circus, leicester_square).
nearby(green_park, leicester_square).
nearby(green_park, oxford_circus).
nearby(oxford_circus, piccadilly_circus).
nearby(piccadilly_circus, charing_cross).
nearby(oxford_circus, charing_cross).
nearby(tottenham_court_road, leicester_square).
nearby(leicester_square, charing_cross).
nearby(tottenham_court_road, charing_cross).
*/

/*
reachable(bond_street, charing_cross).
reachable(bond_street, green_park).
reachable(bond_street, leicester_square).
reachable(bond_street, oxford_circus).
reachable(bond_street, piccadilly_circus).
reachable(bond_street, tottenham_court_road).
reachable(green_park, charing_cross).
reachable(green_park, leicester_square).
reachable(green_park, oxford_circus).
reachable(green_park, piccadilly_circus).
reachable(green_park, tottenham_court_road).
reachable(leicester_square, charing_cross).
reachable(oxford_circus, charing_cross).
reachable(oxford_circus, leicester_square).
reachable(oxford_circus, piccadilly_circus).
reachable(oxford_circus, tottenham_court_road).
reachable(piccadilly_circus, charing_cross).
reachable(piccadilly_circus, leicester_square).
reachable(tottenham_court_road, charing_cross).
reachable(tottenham_court_road, leicester_square).
*/

/*
% copy
reachable(bond_street, charing_cross).
reachable(bond_street, green_park).
reachable(bond_street, leicester_square).
reachable(bond_street, oxford_circus).
reachable(bond_street, piccadilly_circus).
reachable(bond_street, tottenham_court_road).
reachable(green_park, charing_cross).
reachable(green_park, leicester_square).
reachable(green_park, oxford_circus).
reachable(green_park, piccadilly_circus).
reachable(green_park, tottenham_court_road).
reachable(leicester_square, charing_cross).
reachable(oxford_circus, charing_cross).
reachable(oxford_circus, leicester_square).
reachable(oxford_circus, piccadilly_circus).
reachable(oxford_circus, tottenham_court_road).
reachable(piccadilly_circus, charing_cross).
reachable(piccadilly_circus, leicester_square).
reachable(tottenham_court_road, charing_cross).
reachable(tottenham_court_road, leicester_square).
*/

nearby(X, Y) :-
  connected(X, Y, L).
nearby(X, Y) :-
  connected(X, Z, L),
  connected(Z, Y, L).

% non-recursive version
reachable(X, Y) :-
  connected(X, Y, L).
reachable(X, Y) :-
  connected(X, Z, L1),
  connected(Z, Y, L2).
reachable(X, Y) :-
  connected(X, Z1, L1),
  connected(Z1, Z2, L2),
  connected(Z2, Y, L3).

% recursive version
reachable_r(X, Y) :-
  connected(X, Y, L).
reachable_r(X, Y) :-
  connected(X, Z, L),
  reachable_r(Z, Y).

/** <examples>
?- nearby(oxford_circus, Nearby).
?- nearby(bond_street, Nearby).
?- nearby(oxford_circus, Nearby).

?- reachable(oxford_circus, Reachable).
?- reachable(bond_street, Reachable).
?- reachable(oxford_circus, Reachable).
*/
```{swish} swish:long_programme
```

Note

If you look at the markdown source of this document, you will notice that the actual tags of SWISH code boxes placed in this section are different from those listed in the Syntax tabs. Again, this is because multiple boxes cannot be built from a single Prolog source file (due to a label conflict), requiring us to duplicate the long_programme.pl code file.

Such boxes can be made functional by loading them from external Prolog files behind the scenes (instead of sending their contend via URL requests). This file can be composed automatically during the book building process by enabling long file functionality with the build-file parameter of the SWISH code boxes.

build-file: true

Such SWISH boxes will not work when browsing the local build of your book since the underlying code files must be hosted on a server accessible by SWISH. As noted in the Loading Long SWISH Scripts section above, these special code files are uploaded alongside your book, thus enabling the build-file SWISH code boxes to function correctly after publishing your book online. (You can preview the Prolog file loaded by the SWISH code box placed below here.)

% Source path: src/code/long_programme.pl
% Simply Logical, Chapter 1

connected(bond_street, oxford_circus, central).
connected(oxford_circus, tottenham_court_road, central).
connected(bond_street, green_park, jubilee).
connected(green_park, charing_cross, jubilee).
connected(green_park, piccadilly_circus, piccadilly).
connected(piccadilly_circus, leicester_square, piccadilly).
connected(green_park, oxford_circus, victoria).
connected(oxford_circus, piccadilly_circus, bakerloo).
connected(piccadilly_circus, charing_cross, bakerloo).
connected(tottenham_court_road, leicester_square, northern).
connected(leicester_square, charing_cross, northern).

% copy
connected(bond_street, oxford_circus, central).
connected(oxford_circus, tottenham_court_road, central).
connected(bond_street, green_park, jubilee).
connected(green_park, charing_cross, jubilee).
connected(green_park, piccadilly_circus, piccadilly).
connected(piccadilly_circus, leicester_square, piccadilly).
connected(green_park, oxford_circus, victoria).
connected(oxford_circus, piccadilly_circus, bakerloo).
connected(piccadilly_circus, charing_cross, bakerloo).
connected(tottenham_court_road, leicester_square, northern).
connected(leicester_square, charing_cross, northern).

/*
nearby(bond_street, oxford_circus).
nearby(oxford_circus, tottenham_court_road).
nearby(bond_street, tottenham_court_road).
nearby(bond_street, green_park).
nearby(green_park, charing_cross).
nearby(bond_street, charing_cross).
nearby(green_park, piccadilly_circus).
nearby(piccadilly_circus, leicester_square).
nearby(green_park, leicester_square).
nearby(green_park, oxford_circus).
nearby(oxford_circus, piccadilly_circus).
nearby(piccadilly_circus, charing_cross).
nearby(oxford_circus, charing_cross).
nearby(tottenham_court_road, leicester_square).
nearby(leicester_square, charing_cross).
nearby(tottenham_court_road, charing_cross).
*/

/*
% copy
nearby(bond_street, oxford_circus).
nearby(oxford_circus, tottenham_court_road).
nearby(bond_street, tottenham_court_road).
nearby(bond_street, green_park).
nearby(green_park, charing_cross).
nearby(bond_street, charing_cross).
nearby(green_park, piccadilly_circus).
nearby(piccadilly_circus, leicester_square).
nearby(green_park, leicester_square).
nearby(green_park, oxford_circus).
nearby(oxford_circus, piccadilly_circus).
nearby(piccadilly_circus, charing_cross).
nearby(oxford_circus, charing_cross).
nearby(tottenham_court_road, leicester_square).
nearby(leicester_square, charing_cross).
nearby(tottenham_court_road, charing_cross).
*/

/*
reachable(bond_street, charing_cross).
reachable(bond_street, green_park).
reachable(bond_street, leicester_square).
reachable(bond_street, oxford_circus).
reachable(bond_street, piccadilly_circus).
reachable(bond_street, tottenham_court_road).
reachable(green_park, charing_cross).
reachable(green_park, leicester_square).
reachable(green_park, oxford_circus).
reachable(green_park, piccadilly_circus).
reachable(green_park, tottenham_court_road).
reachable(leicester_square, charing_cross).
reachable(oxford_circus, charing_cross).
reachable(oxford_circus, leicester_square).
reachable(oxford_circus, piccadilly_circus).
reachable(oxford_circus, tottenham_court_road).
reachable(piccadilly_circus, charing_cross).
reachable(piccadilly_circus, leicester_square).
reachable(tottenham_court_road, charing_cross).
reachable(tottenham_court_road, leicester_square).
*/

/*
% copy
reachable(bond_street, charing_cross).
reachable(bond_street, green_park).
reachable(bond_street, leicester_square).
reachable(bond_street, oxford_circus).
reachable(bond_street, piccadilly_circus).
reachable(bond_street, tottenham_court_road).
reachable(green_park, charing_cross).
reachable(green_park, leicester_square).
reachable(green_park, oxford_circus).
reachable(green_park, piccadilly_circus).
reachable(green_park, tottenham_court_road).
reachable(leicester_square, charing_cross).
reachable(oxford_circus, charing_cross).
reachable(oxford_circus, leicester_square).
reachable(oxford_circus, piccadilly_circus).
reachable(oxford_circus, tottenham_court_road).
reachable(piccadilly_circus, charing_cross).
reachable(piccadilly_circus, leicester_square).
reachable(tottenham_court_road, charing_cross).
reachable(tottenham_court_road, leicester_square).
*/

nearby(X, Y) :-
  connected(X, Y, L).
nearby(X, Y) :-
  connected(X, Z, L),
  connected(Z, Y, L).

% non-recursive version
reachable(X, Y) :-
  connected(X, Y, L).
reachable(X, Y) :-
  connected(X, Z, L1),
  connected(Z, Y, L2).
reachable(X, Y) :-
  connected(X, Z1, L1),
  connected(Z1, Z2, L2),
  connected(Z2, Y, L3).

% recursive version
reachable_r(X, Y) :-
  connected(X, Y, L).
reachable_r(X, Y) :-
  connected(X, Z, L),
  reachable_r(Z, Y).

/** <examples>
?- nearby(oxford_circus, Nearby).
?- nearby(bond_street, Nearby).
?- nearby(oxford_circus, Nearby).

?- reachable(oxford_circus, Reachable).
?- reachable(bond_street, Reachable).
?- reachable(oxford_circus, Reachable).
*/
```{swish} swish:long_programme
---
build-file: true
---
```

Note

All of the SWISH code and query block functionality is compatible with each other, unless explicitly stated in this document. The following example:

  • prepends a code file,

  • appends a code file,

  • inherits two code blocks,

  • imports three queries, and

  • explicitly states additional two queries.

Moreover, a query is exported into this SWISH code block. We also request to build an external Prolog file in case the entire content is too long to be loaded directly (you can preview the built file here). Finally, we hide the explicit /** <examples> ... */ query block since it will be overridden by all the other queries anyway.

The following query

?- exists_extension(jupyter_book, Exported).

is exported into the SWISH code block displayed below.

exists_extension(jupyter_book, prolog).
exists_extension(jupyter_book, markdown).

The following query

```{swish-query} swishq:long-complex
---
source-id: swish:long-complex
---
?- exists_extension(jupyter_book, Exported).
```

is exported into the SWISH code block displayed below.

```{swish} swish:long-complex
---
inherit-id: swish:iq swish:hide
source-text-start: prepend_code
source-text-end: append_code
query-id: swishq:inline swishq:display swishq:display-in
query-text: |
  ?- exists_extension(jupyter_book, HQ_1).
  ?- exists_extension(jupyter_book, HQ_2).
hide-examples: true
build-file: true
---
exists_extension(jupyter_book, prolog).
exists_extension(jupyter_book, markdown).

/** <examples>
?- exists_extension(jupyter_book, ExplicitQuery).
*/
```

See also

For more information about the SWISH module see its technical documentation.