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.

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…

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.

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.

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.

It was just for saying…it was not meant to be a serious proposal as one of the many PIPs you’re submitting…personally I feel that the currents have already led me far too astray from what my original interests were so…

The world is still in order then. And benign deities oversee

PS: I notice that lately you always add an image when you speak to me…thanks…you know that we italians are a “visual” culture…if you didn’t add an image I wouldn’t understand a thing

A late contribution to the discussion:

Since optimized (compiled) arithmetic support was added, user defined arithmetic functions are implemented using library(arithmetic). But, as noted above, non-numeric arguments and return values are not supported. Any syntax issues can be dealt with using term/goal expansion, but errors will be generated for non-numerics at evaluation time .

So a couple of years ago I experimented with an extended clone of arithmetic which did support user defined types - see arithmetic_types.

Also included are a small set of example types, one being type_stringy, so:

?- use_module(library(type_stringy)).
true.

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

?- "a" < "b".
true.

% `\\` is the concat operator
?- X is "foo" \\ "bar".
X = "foobar".

% slicing and indexing using `op(100, yf,  [])`
?- S is "abc"[0].
S = a.

?- S is "abc"[-1].
S = c.

?- S is "abc"[1:E].
S = "bc",
E = 3.

?- P is find("cd","abcdef").
P = 2.

But my main motivation was to support N-dimensional numeric arrays for representing a linear system of equations that could be solved using Cramer’s Rule, e.g, solving:

x + y + z + w = 13 
2x + 3y − w = −1 
−3x + 4y + z + 2w = 10 
x + 2y − z + w = 1

with

?- set_prolog_flag(prefer_rationals,true), use_module(library(type_ndarray)).
true.

?- _A is ndarray([[1,1,1,1],[2,3,0,-1],[-3,4,1,2],[1,2,-1,1]]),
_B is ndarray([[13],[-1],[10],[1]]),
Vs is ndarray([[X],[Y],[Z],[W]]),
Vs is dot(inverse(_A),_B).
Vs = #(#(2), #(0), #(6), #(5)),
X = 2,
Y = 0,
Z = 6,
W = 5.

I don’t think it’s inappropriate to support these extended types in arithmetic evaluation because often the arguments, e.g., an index into a string, or the result, e.g., the position in a string of a substring, are numeric, so it’s useful to be able to mix numeric and non-numeric values in a single expression using an extended set of functions.

I do think this is all too immature to be considered PIP worthy and I’m unaware of any similar capabilities in other Prolog’s. But the ability to port something like this would be simplified if the underlying foundation was somewhat “standardized” via the PIP process (perhaps some already are), e.g., things like

  • term and goal expansion
  • bracket style operators (to support index and slice syntax)
  • arithmetic_function directive equivalent
  • predicate_property equivalent for finding loaded (polymorphic) arithmetic functions

But IMO I think Peter’s correct in stating that much of this can be accommodated by “a small matter of programming, and not a reason for major changes one way or the other” (ignoring performance issues, of course?).

Just a quick comment: note that you can make and discuss the PIP proposals directly on the PIPs discourse:
New PIP proposals - Prolog Community.

1 Like

cmpr/2 , minr/2 and maxr/2 are documented in the SWIP Reference Manual under arithmetic functions (Sect 4.27). I too would like to see a PIP on IEEE 754 support which includes evaluation of mixed mode expressions (ints, bigints, rationals and floats). The SWIP development attempted to maintain compatibility with the Eclipse proposal so I think the bar of at least two implementations has been achieved.

The ‘r’ suffix stands for real. The intent of cmpr/2 , minr/2 and maxr/2 is to support mathematically correct comparison of any two real numbers (extended with the infinities) independent of their physical representation (numeric sub-type). I tried to argue that this should be the default behaviour of arithmetic but since ISO Prolog defined a sometimes mathematically “incorrect comparison” (convert non-floats to floats), the three functions are strict additions for upwards compatibility reasons. So I’d also like to see that for discussion in a PIP context.

More useful input from Richard O’Keefe in ISO Discussion, 2015, Section 6, Numbers.