Search code examples
prologswi-prolog

Prolog HTTP dynamically add links to html


I'm new to prolog and declarative programming and I struggle to achieve the following.

I'm following this tutorial and now would like to show a number of links on a page. Which links are to be displayed is dependent on certain facts / variables.

Here's my current code:

link_collection(Request) :-
http_parameters(Request,
[
    foo(Foo, [optional(true)])
]),
reply_html_page(
    [title('Dynamic Link Collection')],
    [
        a([href='/questionalice'], 'Question Alice'),       /* Should only show if has(investigate, body) is true */
        a([href='/questionbob'], 'Question Bob'),          /* Should only show if Foo = bar */
        a([href='/investigatebody'], 'Investigate Body')   /* Show always */
    ]
).

note that the number of "permutations" doesn't allow me to just "or" the link_collection statements. Also I would like the conditions to be arbitrarily complicated.


Solution

  • Your question can be answered in a quite general context, that is, even without taking into account the concrete use case of HTTP.

    The general question seems to be: How can I dynamically select a subset of some available options.

    For this, suppose that you represent each link not simply as "itself", but rather as a pair of the form Link-Condition, with the interpretation that Link should only be included if Condition is true.

    Let us first consider the conditions we want to express, and define when they are true. Importantly, your conditions also depend on the value of Foo, so this has to be taken into account:

    is_true_with_foo(_, has(investigate, body)) :- has(investigate, body).
    is_true_with_foo(Foo, Foo = bar)            :- Foo = bar.
    is_true_with_foo(_, true).
    

    So, this describes when a particular condition is true, also depending on the value of Foo.

    Now, your sample conditions can be represented as follows:

    links_conditions(Foo,
        [
            a([href='/questionalice'], 'Question Alice')-has(investigate, body),
            a([href='/questionbob'], 'Question Bob')-(Foo = bar),
            a([href='/investigatebody'], 'Investigate Body')-true
        ]).
    

    To describe a subsequence of a list, consider using a DCG ().

    For example:

    links_subset([], _) --> [].
    links_subset([L-Cond|Ls], Foo) -->
            (   { is_true_with_foo(Foo, Cond) } ->
                [L]
            ;   []
            ),
            links_subset(Ls, Foo).
    

    You can now call:

    ?- links_conditions(Foo, LCs0),
       phrase(links_subset(LCs0, no), LCs).
    

    and obtain, in LCs the remaining links. In this case:

    LCs = [a([href='/questionalice'], 'Question Alice'),
    a([href='/investigatebody'], 'Investigate Body')].

    So, we can use the resulting links in our reply:

    link_collection(Request) :-
            http_parameters(Request, [foo(Foo, [optional(true)])]),
            links_conditions(Foo, LCs0),
            phrase(links_subset(LCs0, Foo), LCs),
            reply_html_page([title('Dynamic Link Collection')], LCs).
    

    Note how Foo is passed along in these predicates.

    P.S.: Your example snippet has elementary syntax errors, so I doubt that your code works in any way.