Hi Kashyap

Familiarizing with the concept of prolog might help, but studying prolog in detail might just add to the confusion, as prolog uses a quite different syntax than pilog (which is embedded into picolisp, but kinda it's own language).

You posted a select query, it is likely the most complex pilog predicate, but also likely the most used when working with picolisp database. In the reference is a document just about select: https://software-lab.de/doc/select.html
(it is in @doc/ within your picolisp directory).

At the top comes the picolisp function (?) which executes the pilog query it gets via arguments.

(?
(?) is interactive, so handy in the REPL, but in code you usually want to use (pilog) or (solve).
(solve) is kinda like (collect) as it produces a list of results.
(pilog) is like (iter), as you can hand it a function to map over the results, e.g. changing database records according to some pilog query.

The important detail here is that the picolisp pilog-interface-functions (?, pilog, solve, query and prove) handle their arguments differently, some expect directly a pilog-query (no quoted), some expect a quoted pilog query (so instead of a quoted hardcoded query it could be a picolisp function returning the query). This confused me at first. Read the reference carefully and then you see the different usages and intentions.

In the function list at the end of the reference <https://software-lab.de/doc/ref.html>you have the pilog section, it contains a mix of picolisp functions (mostly the ones without a / in the name) and pilog predicates and statements. Be careful to see if a piece of code is pilog and belongs into a pilog query,
or if its picolisp code working with a pilog query.

E.g. (goal) and (fail) are picolisp helper functions to construct a pilog query (e.g. to generate a query from code, instead of hardcoding it).

Then comes a (optional) listing of pilog unification variables (aka: parameter bindings). Most picolisp pilog-interface functions take those as the first argument(s), some expect to be part of the quoted list handed to them. Strictly speaking they are not pilog and not exactly part of the pilog query, but binding the parameters for use within pilog interpreter.

Remember: pilog is an interpreter running inside the picolisp interpreter, and it has not automatic access to picolisp variables. You have to bind the variables as such pilog parameters, or dynamically run picolisp code within the prolog interpreter: "When the CAR of a Pilog clause is the symbol |^|, then the CDDR is executed as a Lisp |prg| body and the result unified with the CADR". See the last part of the "Pilog (PicoLisp Prolog)" <"Pilog (PicoLisp Prolog)"> chapter in the ref, means ^ is not a function, but a read-macro for the pilog interpreter!

The "unification variables" are rather easy, despite the unwieldy words:
Rule 1: pilog variables have to start with @, e.g. @Nr.
Rule 2: pilog variables never change their value. Because pilog (prolog) does not execute statements, but finding solutions to a bunch of "facts". Using the "unification variables" we define some fixed facts by setting those pilog variables to picolisp values ("unifying" the pilog variables with the picolisp environment).

Apart from that, it is just like the variable list in a (let):
@pilog-variable-to-set <picolisp-expression-to-produce-the-value>

@Nr (and *CuSuNr (cons @ T))
Set @Nr to: if picolisp variable *CuSuNr is NIL, to NIL, else to (cons *CuSuNr T).
So @Nr is NIL or e.g. (45123 . T)

@Nm *CuSuNm
@Tel *CuSuTel
@Plz *CuSuPlz
@Ort *CuSuOrt
@Mob *CuSuMob
Easy, just setting the @pilog-variable to the value of the picolisp variable.

After the "unification variables" (or pilog query parameters, as I would call it), comes the pilog query. Here a select/3 is used. Don't forget to read the dedicated reference! <https://software-lab.de/doc/select.html>

(select (@@)
The first argument to our select query is (@@), again list of unification variables, but this time the unification variables which should be communicated from the pilog environment back to the picolisp environment. So basically a kind of return values, just a list of pilog variables we like to get returned, nothing more.
These are pilog variables, so all elements must start with @.
Often you need only one, then it is kind of habit to use the iconic variable name @@.

((nr +CuSu @Nr) (nm +CuSu @Nm) (tel +CuSu @Tel)
            (plz +CuSu @Plz) (ort +CuSu @Ort) (mob +CuSu @Mob) )
After the list of unification variables, selects wants a list of "generator clauses".
This is a list, therefore one pair of parentheses around the whole thing ;-)
Within this list, we may have one or more generator clauses, which are again written as lists itself.
(Pilog has really many parens).

A generator clause can be very short and easy, but also long and funny :-D
Check out the "Generator clauses" section in the select predicate reference <https://software-lab.de/doc/select.html#gencl>.

In its most basic form,a generator clause looks like arguments to a db/3 pilog predicate (db/3 is an alternative to select, the pilog db query is the equivalent of the picolisp collect and db functions).

(nr +CuSu @Nr)
Go through the 'nr index of '+CuSu, bind the value of the 'nr to the @Nr pilog variable.
But we already set the value of @Nr with the parameters at the start!
It is either NIL or (cons *CuSuNr T). And pilog variables never change their value. So this generator actually means: Go through the 'nr index of '+CuSu, find all objects (database entries) which match (cons *CuSuNr T) on the 'nr index.
In picolisp you would do: (collect 'nr '+CuSu (cons *CuSuNr T))

Same with the other generator clauses:

(nm +CuSu @Nm)
(collect 'nm '+CuSu *CuSuNum)

(tel +CuSu @Tel)
(collect 'tel '+CuSu *CuSuTel)

and so on...

But:
- generator clauses work on database indices (indexes), so properties used in generator clauses have to be indexed (e.g. +Key, +Ref, +Idx, ...). - we have a list of generator clauses, they're logically connected by AND, so the results have to fit all the clauses!

This should make obvious why pilog is usually faster than picolisp database queries: the generator clauses, which generate the set of possible results, are working solely on the database index, usually without having to fetch the database object itself into memory, so this is really quicker instead of fetching all the objects and examining if they fit all the criteria.

It's good if you use as many generator clauses as possible, whenever indexed properties fit your query. Pilog automatically tries to identify the index which gives the best result to your query (the one which passes all the other criteria too), this way it optimizes query speed further when possible. Important is, that the results have to have all the properties used in the generator clauses, else they're not found.

Generator clauses can be way more involved, e.g. using +Link and +Joint relations for indirect access. It is also possible to have within a single generator clauses multiple clauses which are then connected by OR logic. See the reference of the select predicate <https://software-lab.de/doc/select.html#combined>.

After the list of generator clauses, may come one or multiple filter clauses (optional).

(select (<list return variables>) (<list of generator clauses>) <filter clauses>)
The filter clauses work on the results produced by the generator clauses.
Each result of the generator clauses is bound to the primary return variable, so @@ in this case.
The filter clauses do then further checks.

(range @Nr @@ nr)
Is the value of the 'nr property of the result @@ within range (cons *CuSuNr T) ?
T is the maximum, so 'nr must be equal *CuSuNr or higher.

(tolr @Nm @@ nm)
Let's apply picolisp function (fold) (= removes all special characters, change to lowercase) to @Nm, and then check if it is a substring of the 'nm of @@ or if 'nm is a Soundex match (sounds similar) to @Nm.

(fold @Tel @@ tel)
Let's convert @Tel to lower case and compare with 'tel of @@.
@@ is a valid result if they're equal.
So it is a bit like (collect 'tel '+CuSu (fold *CuSuTel))

(head @Plz @@ plz)
Does 'plz of @@ start with *CuSuPlz ? (without folding)

(part @Ort @@ ort)
Let's fold, and then check if 'ort property of @@ contains the substring *CuSuOrt.

(fold @Mob @@ mob)
Let's fold, and check if 'mob of @@ starts with *CuSuMob

That's it.
A bit tricky at first to see all the different concepts and functions and how they're used, but once you see the different concepts and clauses, it becomes mostly obvious.

Pilog and especially select/3 allows for construction of very complex queries which in pilog are still easy to read. (You don't want to do the same in traditional SQL, would often be hundreds of lines of code).

Kind regards,
beneroth

On 21.10.19 21:14, C K Kashyap wrote:
Thanks Alex,
So, would you suggest that I familiarize myself with prolog before venturing deeper into app development in picolisp? I think it will be helpful for me if I could get an english translation of this?
(?
      @Nr (and *CuSuNr (cons @ T))
      @Nm *CuSuNm
      @Tel *CuSuTel
      @Plz *CuSuPlz
      @Ort *CuSuOrt
      @Mob *CuSuMob
      (select (@@)
         ((nr +CuSu @Nr) (nm +CuSu @Nm) (tel +CuSu @Tel)
            (plz +CuSu @Plz) (ort +CuSu @Ort) (mob +CuSu @Mob) )
         (range @Nr @@ nr)
         (tolr @Nm @@ nm)
         (fold @Tel @@ tel)
         (head @Plz @@ plz)
         (part @Ort @@ ort)
         (fold @Mob @@ mob) ) )

Regards,
Kashyap

On Mon, Oct 21, 2019 at 10:00 AM Alexander Burger <a...@software-lab.de <mailto:a...@software-lab.de>> wrote:

    On Mon, Oct 21, 2019 at 07:12:32AM -0700, C K Kashyap wrote:
    > Finally - its good to see the values :) ... I used (show @@).
    > I see, so @@ is just a "variable" introduced in the select
    function - i got
    > a little confused by (@@) - I thought it was a function call -
    but looks
    > like select takes the argument unevaluated :) (love the (vi
    'select) )

    We cannot talk about "evaluation" in Pilog, as this is no Lisp
    despite it uses
    s-expressions.

    It is all about "unification", i.e. values are matched with
    patterns containing
    variables. And a variable is recognized by Pilog because it starts
    with "@". In
    real Prolog variables are recognized because they start with an
    uppercase
    letter.


    > I noticed that the variable name has to start with @ - It does
    not work if
    > I use a name without @ - why is that?

    Exactly. This is how Prolog works. Perhaps you may want to look at
    some intro
    like https://www.metalevel.at/prolog - Prolog is a beast very
    different from
    other languages.


    Also, I noticed that functions in the
    > form library have their parameter names in the string format
    (for example (de
    > form ("Attr" . "Prg")) - I am curious about that too.

    These are transient symbols used for privacy. See
    https://software-lab.de/doc/faq.html#problems

    ☺/ A!ex

-- UNSUBSCRIBE: mailto:picolisp@software-lab.de
    <mailto:picolisp@software-lab.de>?subject=Unsubscribe

Reply via email to