Is a PIP for Syntax extensions necessary?

I have a question, does SWI-Prolog not anymore use
their dict syntax in the Janus interface:

> import janus_swi as janus
> janus.query_once("Y is X+1", {"X":1})
{'Y': 2, 'truth': True}

I don’t see _{...} anymore. When and how did this
happen? I was just thinking whether a Syntax extension
PIP is necessary. Such a PIP isn’t listed. Is SWI-Prolog

safe, against parsing problems, when it still has block
operators in the background? Like can one mix and match
code that uses Janus interface with the “new” dicts with

other code that uses the SWI-Prolog dicts based on _{...}
which we might now term the “old” dicts. How do you access
and manipulate the “new” dicts, do the “old” operations work?

Edit 10.08.2024
Anything that touches or depends on syntax is a complete
can of worms. The ISO commitee was not able to find an initial
english natural language specification wording for character

look-ahead, so that we find now character look-ahead in
Prolog compounds practially implemented by all Prolog systems,
but character look-ahead interpretation may differ for

negative numbers, like here:

/* SWI-Prolog */
?- X = - 1^2, write_canonical(X), nl.
-(^(1,2))

/* Scryer Prolog */
?- X = - 1^2, write_canonical(X), nl.
^(-1,2)

Ok, my bad the above is Python code and
not Prolog code, right? But how does XSB do it?

Ok the docu say:

Compatibility to the XSB Janus implementation
For this reason we support creating a Python dict both from
a SWI-Prolog dict and from the Prolog term py({k1:v1, k2:v2, ...}) .
https://www.swi-prolog.org/pldoc/man?section=janus-vs-xsb

Does this affirm or reject the need for syntax extension PIP?
Why not just use “new” dicts on both sides? Forget about
the py tag, its anyway always py. Does XSB “new” dict wrapped

in py compound have access and modification operations. Wouldn’t
it be enough to provide operations without the py compound wrapping.

We are discussing a PIP on representing “objects”. That is the general issue that you want a good syntax for representing key-value sets with a number of operations on this. The Janus design took some shortcuts, but we would like to arrive at something close. The issue is not easy. There are the key-value sets, the issue of strings vs constants and (lack of) atom-GC and possibly limited atom capabilities (e.g., length). If you want to stay strictly within ISO, it gets a little awkward. Part of the discussion is on how far we want to extend ISO and/or demand features not demanded by ISO, e.g., garbage collected atoms without limitations. Other issues we still mostly ignore are the non-normal floats (NaN, +/-Inf) that cannot be represented as ISO Prolog numbers.

(Nothing of importance)

Time will tell. I think certain PIPs may require specific non-functional requirements to be applicable. Note that with PIPs we do not expect all Prolog systems to follow. They describe the consensus reached by at least two systems on how to deal with “something”, where we hope it is an inspiration to others.

Distinguishing strings from atoms has both a technical reason and plays a role in comfortably representing dynamically typed data for exchange or defining a DSL. The need for the technical reason can possibly be avoided. The additional data representation it provides is harder. Most languages always demand strings to be quoted and use unquoted identifiers for symbols. We do not have that. Our symbols may be unquoted when they satisfy certain syntactic constraints. We cannot distinguish 'null' from null. In Janus, we decided that the atom none maps to the Python string "none" and @(none) maps to the Python object None. If we embraced strings, we could have used "none" and none.

SWI-Prolog does this for JSON, but the problem is that in Prolog one often wants to see an atom instead of a string.

For short, I’m still unhappy with the situation. ECLiPSe has used strings from its early days and didn’t have atom-GC (do not know the current state), so their applications have typically been designed to deal with this. Most SWI-Prolog applications use atoms for text as they have no limits are are garbage collected.

Why would that work? The fact that we have strings doesn’t imply arithmetic should do something with them. To transparently support “a” as the character code for ‘a’, it does accept one-character strings as arithmetic constants. That is all though.

I only wanted to highlight that there is spectrum between
“embrace” and “fully embrace” of the string type. I think its
correct if I say that SWI-Prolog doesn’t “fully embrace” the

string type, since the string type cannot be used in evaluable
expressions. Whereas for other programming languages its
quite common that the string type appears in evaluable

expressions. For example Python can do it:

/* Python 3.13.0b4 */
>>> "foo"+"bar"
'foobar'

They even were creative and gave it some new twists:

>>> "foo"*3
'foofoofoo'

Disclaimer: I don’t know what the Janus interface allows…

Edit 12.08.2024
That this here works isn’t required by the ISO core standard:

/* SWI-Prolog */
?- X is [19].
X = 19.

For example its not supported by Scryer Prolog:

/* Scryer Prolog */
?- X is [19].
   error(type_error(evaluable,[]/0),(is)/2).

Its a remanent when there was no character code literal,
and one could use a singleton character code list, in an
evaluable expression to get a character code. But the

ISO core standard defined character code literals by
the 0' syntax. And exactly this remanent somehow prevents
to “fully embrace” the string datatype as is done in other

programming languages such as Python.

You wrote Python would be a dead language in five years (your estimate), I don’t understand what there will be to “fully embrace” then…or why you even seem to be concerned…

(Nothing of Importance)

I have always considered this gimmicky and rarely useful.

Either way, what Python really has is duck typing (“if it walks like a duck…”) so you can make your objects behave any way you want. For example, you can emulate numeric types:

For instance, to evaluate the expression x + y, where x is an instance of a class that has an __add__() method, type(x).__add__(x, y) is called.

This is very similar to C++ operator overloading, as far as I can tell, but others should know better.

It was demanded many times for Prolog. And the topic
pops up from time to time on SWI-Prolog discourse as well.
The problem is a concat operator was already available

in SQL, which is from 1970s. It was the (||)/2 operator but
it is now also often the (+)/2 operator. So you cannot write
Prolog queries the same way as you can write SQL queries.

A fully embraced string type would give this possibility, with
atoms alone its very difficult, for example this here gives a
number and not the text “pi”:

?- X is pi.
X = 3.141592653589793.

But with a fully embraced string type
we could also have the text “pi”:

?- X is "pi"
X = "pi"

This has nothing to do with the string type, but with the fact that is/2 is deeply restricted to numeric types in SWI-Prolog. Some other Prolog systems do that differently. That has been discussed.

So you say my “could” is wrong? What makes you think
a Prolog system couldn’t do that? I don’t think is/2 is deeply
restricted to numeric types in SWI-Prolog or any other Prolog

system. After all we are talking about PIPs that extend
core Prolog. You only need a switch between values and functions.
Its relatively straight forward to support this here:

?- X is "foo"+"bar".
X = "foobar".

You would change the notion of value, i.e. those Prolog terms
that evaluate to itself, to not only cover numbers but also
strings. Its as easy as that. Atoms and compounds that

refer to evaluable constants and functions wouldn’t change
in any way, since you have two types atom and strings. And
atoms are still the functors of compounds and

not strings. You would get a quite consistent is/2. You would
also get (=:=)/2 and (<)/2, just like in Python:

/* Python 3.13.0b4 */
>>> "foo"+"bar" == "foobar"
True

>>> "a" < "b"
True

Disclaimer: Again I don’t know what the Janus interface allows…

Here is the definition of a PIP:

discussion about and development of Prolog Improvement
Proposals (PIPs). PIPs describe important extensions to
ISO-Prolog systems that are not (yet) included in the
Prolog ISO standards. These extensions may affect the
Prolog language itself or they may be implemented as
libraries or packages.
https://prolog-lang.org/ImplementersForum/ImplementersForum.html

You are commiting a similar fallacy like @anon419083 . Whereas @anon419083
thought I have single ultimate PIP in mind, wanted to lure me into this
trap, and all my discussion would be about this ultimate PIP.

Here @jan fallacy is I would talk about SWI-Prologs current affair,
or would be bound by SWI-Prolog current limitations, whereas I am
not at all interested in any status quo of an existing Prolog system.

The word “could” is about painting a picture of the future. You
can also paint different pictures of the future. In effect you could
have competing PIPs. I think Python was ticking along competing

PIPs on many occasions. They even kept competing PIPs alive
for a while. And only later decided in favor of one or the other.
So you could have two competing PIPs:

  • PIP 901: Embrace strings
    String literal, unify, (==)/2 and (@<)/2

  • PIP 902: Fully embrace strings
    String is/2, (=:=)/2 and (<)/2

Got it? Its even not important that everybody understand every
PIP or is interested in every PIP. If you can’t grasp a picture of
the future, well then if you are totally desparate you might

throw an egg at it or otherwise simply keep shut.

I would argue that this use of “+” is a mistake because “+” is commutative with ordinary arithmetic but not with strings (yes, yes, I know about floats and “+”). So it would have been better to use a different operator, such as “||”.

Supporting more operators with is/2 is just a small matter of programming, and not a reason for major changes one way or the other.

There’s another whole discussion about supporting a more functional style of programming in Prolog (e.g., it would be nice to be able to write foo(X+1,...) instead of X2 is X+1, foo(X2, …)`). Note that the dot-syntax for fields does support a more functional style.

1 Like

I’ve actually used it a few times in real production Python code; typically where the string is a single character (e.g., to fill a field with blanks).
BTW, the * operator is commutative here (unlike + for strings); you can write either "foo"*3 or 3*foo".

But substring operations (e.g., "foo"[1:]) are far more useful.

As for things like __add__ in Python, see operator — Standard operators as functions — Python 3.12.5 documentation … tl;dr: the operators such as “+” and “.” are syntactic sugar for function calls (e.g. __getitem__, __setitem__, __getattr__, __getattribte__, __attr__). NB the two different __ functions for attributes.

One could also conceive a PIP for partial strings. This idea was
pursued by Scryer Prolog and by Trealla Prolog, so the PIP would
satisfy the criteria of having two Prolog systems implement it:

  • PIP 903: Partial strings
    Compact and shared char lists.

Originally Java had something to this end in their substring() method,
but over the time they abandoned it. Since they noticed that malicious
code could lock and prevent from garbage collection large amounts

of memory by a small substring(). So you find this sharing semantics
not anymore in Java. But was it worth the effort for the two Prolog systems?
Currently Partial strings seem not to be the banger when they are

combined with some DCG pipe dreams. Here is a test case:

data("{                                   \"a\":123 }").

And some benchmarking results:

/* Scryer Prolog 0.9.4-135 */
?- time((between(1,1000,_), data(X), json_chars(Y,X,[]), fail; true)).
   % CPU time: 0.283s, 2_506_022 inferences
   true.

/* Trealla Prolog 2.55.19 */
?- time((between(1,1000,_), data(X), json_chars(Y,X,[]), fail; true)).
% Time elapsed 0.235s, 2568003 Inferences, 10.937 MLips
   true.

/* Dogelog Player for Java 1.2.1 */
?- time((between(1,1000,_), data(X), json_atom(Y,X), fail; true)).
% Zeit 40 ms, GC 0 ms, Lips 12502725, Uhr 13.08.2024 08:43
true.

/* SWI-Prolog 9.3.8 */
?- time((between(1,1000,_), data(X), atom_json_term(X,Y,[]), fail; true)).
% 44,998 inferences, 0.016 CPU in 0.006 seconds (281% CPU, 2879872 Lips)
true.

Still the Prolog systems that forget about DCG pipe dreams and
partial strings, and write a dedicated parser in Prolog using some
more traditional top down parsing approach are noticably faster.

As you seem to have taken this PIP thing quite seriously: adding a queue library to Swi-Prolog (the matter has been already discussed) could be considered a PIP? What do you think? Or should PIPs refer to the language more than to one of its implementations? Thanks

Not directly. It requires finding some (at least one) other Prolog system willing to specify the interface of this library. As we have seen, several systems have a queue library, so the most obvious thing to do would be to invite people from these systems and try to reach an as wide as possible agreement. If anyone likes doing that …

And yes, PIPs can be about functional or non-functional language aspects as well as libraries.

One could PIP-ify this one:

ISO/IEC DTR 13211–5:2007 Prolog multi-threading support
Paulo Moura ed. - October 28, 2008
https://logtalk.org/plstd/threads.pdf

Among things like mutex, thread, etc… it has also queues.
But maybe they are not the same queues that @anon419083 has
on his mind. On the other hand the above style of multi-threading

support is not uncommon among Prolog systems that have
multi-threading. I guess there is more than one Prolog systems
implementing it already, but maybe not to the letter as the

above document describes it. So we could have PIPs, I am on
purpose defining smaller PIPs, an approach which is also sometimes
seen for Python PEPs, for the following topics:

  • PIP 801: Threads
    Split off from Paulo Maura’s multi-threading document.

  • PIP 801: Mutex
    Split off from Paulo Maura’s multi-threading document.

  • PIP 802: Message queues for multi-threading
    Split off from Paulo Maura’s multi-threading document.

  • PIP 803: Simpler queues not for multi-threading (@anon419083’s ?)
    Other sources here.

Message queues for multi-threading are quite different than
the simpler queues not for multi-threading. Message queues
for multi-threading have to solve the communication problem

between threads, often solved by message copying. But then
the above PIPs wouldn’t help so much a tight integration with
Python, since Python is very peculiar. It has as a main entry

point a single thread with an event loop. Just like JavaScript
which has the same peculiar feature. So we would need further
PIPs to keep up with Python:

  • PIP 701: Tasks
    Stackfull and stackless cooperative coroutines.

  • PIP 702: Events
    An event loop to “asyncify” file system access, network
    access, queues, terminals, windowing systems, etc…