Subject: Re: [SWIPL] no copy semantics for PlTerm, in the C++ interface

From: Volker Wysk <[email-masked]>
Date: Tue, 6 Nov 2001 18:48:36 +0100 (CET)

Hi

I've thought over it, and found that it's possible to integrate the prolog
terms cleanly with C++'s notion of values, references and pointers.  I'm
impressed of how flexible C++ is. In particular, one could

- define a class TermRef which behaves exactly like some hypothetical type
  Term&. This is the closest we can get to terms as values.

- define a class TermPtr which behaves like Term*. This would be the
  equivalent of a stack index (a value of type term_t).

- define a class TermPtrRef which behaves like Term*&. This would allow
  access to the local stack cells as if they were pointers to terms.

Notice that a TermRef not actually _is_ a reference to any object, but it
has the same semantics as one. Similarly for a TermPtr. It does not point
to any C++ object. But it behaves exactly like if it would.

One could manipulate terms very easily using TermRefs - just as if they
were references to terms. For instance, we can define comparision
operators for TermRefs which actually compare the terms, according to the
standard term order:

void some_predicate(const TermRef t1, const TermRef t2)
{
  if (t1 > t2) ...

On Tue, 6 Nov 2001, Jan Wielemaker wrote:

> >So the cells on the local stack are references to terms, and PlTerm
> >objects as well as term_t values are references to references to terms.
> >The comparision functions - PlTerm::operator==, PlTerm::operator> and so
> >on - are wrong in this case. They don't compare the values of PlTerm
> >objects (which are indexes for the local stack), but the terms refered to
> >by the term references refered to by the indexes. [ Isn't declarative
> >programming much less of a headache than this? ;-) ]
> >
> >For instance, if you stored PlTerms in a sorted container, such as a
> >map<>, you could affect the comparision order of the PlTerms by operations
> >which either change the terms referred to indirectly, or the term
> >references refered to. You would mess up the container as a side effect.
> >This would make it impossible to store PlTerms in sorted containers.
>
> Hmm.  This is getting tricky.   The relation between values and pointers
> as seen in C(++) is quite different in Prolog.   A PlTerm should be seen
> as a variable that can hold Prolog terms and that can be assigned using
> one of the PL_put_*() functions.  Prolog terms can be references to
> `the real thing' and so on, but how many indirections there are should
> not matter to the programmer, whether in C(++) or in Prolog.  (...)

Indirections _do_ matter to a C++ programmer. A pointer, a value and a
reference are very different things in C++. Of course it would be nice to
have terms as values. But it's not possible, because the stack cells are
pointers, not values.

> Unification is an alien operation to C++ and
> therefore regarding it some sort of assignment using the = operator has
> been a wrong choice (agreed).

Well, from a C++ perspecitve, unification is an operation that modifies a
value (the term).

> I know next to nothing about STL and I'm wondering how much we have to
> sacrifice and what we get in return making PlTerm's operators compliant
> to STL.

It's not only the STL. Of course you can define overloaded operators to do
anything. But this can be very confusing, and has some other
deficiencies... You should overload operators only if the behave like the
built in operators.

> Do you have any scenarios with nice looking STL code doing
> plausible things with Prolog terms?  Normally, all you do is create
> terms, call predicates and map the results to something you want to
> use in C++.  Lifetime of Prolog terms from C++ are a bit hard to grasp
> for the simple soul and operating them is far easier in Prolog.

True, since Prolog has value semantics, whereas C++ has reference
semantics. Mapping terms and such to C++ types as described above, would
make it easy to understand for C++ programmers.

> Note that you
> can use PL_put_* only on PlTerms you created yourself, *not* on the ones
> you get as predicate arguments.

But you can still unify the terms passed as arguments? This means the
arguments are of type "Term&" / "TermRef" from C++ perspective. Otherwise
it would be "const Term&" / "const TermRef".

> three versions of them ...  Besides, its not true.  If I do
>
>   term_t x = PL_new_term_ref();
>   PL_put_atom_chars(t, "hello");
>
> x is an offset in the local stack and at that location *is* the atom
> 'hello' (no, not the text, an atom is an integer with some tag-bits
> and an offset into the atom-table).

Er, but you can still put a new value there? Such as

term_t x = PL_new_term_ref();
PL_put_atom_chars(t, "hello");
PL_put_atom_chars(t, "goodbye");

> >I've taken a look at SWI-cpp.h. It doesn't look very difficult to do this.
> >I'd volunteer to do the C++ things.
>
> I'm looking forward to a new SWI-cpp.h that makes things look nicer.
> Getting rid of PlTermv would be a good start.  Before doing so, make
> sure you understand all issues around term_t.

I think I do. But please answer the two questions above.

> Maybe you can start a page on the twiki web holding issues and the
> current version?

I'll just make a prototype next.

> I do propose to use a different name, so old code
> can keep using SWI-cpp.h.

Yes. I'll name it "prolog.h".

- Volker