Edge case: dicts and compound_name_arity/3

Retrieve the “dict functor” by defining this predicate:

dict_functor(F) :- compound_name_arguments(_{},F,_).

Then

?- dict_functor(F), compound_name_arity(C,F,5),compound_name_arity(C,N,5).
F = N, N = C'dict',
C = C'dict'(_32692, _32694, _32696, _32698, _32700).

The compound term assembled by compound_name_arity/3 using the dict functor as Name argument is, indeed, a compound term, but it doesn’t seem to be a dict because

  • it’s not written as such and
  • has freshvars as keys.

However, is_dict/1 thinks otherwise:

?- dict_functor(F), compound_name_arity(C,F,5),
    compound_name_arity(C,N,5),is_dict(C).
F = N, N = C'dict',
C = C'dict'(_36122, _36124, _36126, _36128, _36130).

It is indeed confirmed to be a dict. :thinking:

That’s probably not right.

Analysis works as expected (dropping information is consistently easier than making up missing information):

?- compound_name_arity(foo{x:1,y:2},Name,Arity).
Name = C'dict',
Arity = 5.

P.S.

compound_name_arity/3 and compound_name_arguments/3 use Name in the argument list instead of Functor. I like that … using functor in 2020 is confusing, seeing how Category Theory is now mainstream. I also note that functor was already used in the paper “General Theory of Natural Equivalences” by Samuel Eilenberg and Saunders MacLane, 1945. Get out of the way, Prolog!

Possibly we should deny functor/3 and compound_name_arity/3, etc. to compose or even decompose dicts. No sure. Doing so will require some code that now happily runs over all possible terms to require additional clauses for dicts. On the other hand, some of these generic routines may corrupt dicts. As is, is_dict/1 tests the functor and arity (to be odd). A full test requires checking all keys to be valid and sorted. This is an O(n) operation which I think should be avoided.

I think an appropriate warning in the manual should be good enough. I can add a couple of lines.

Then again … if the dict “opaque data structure” were classified as blob/atomic instead of compound, these problems would go away in one swoop. But maybe this would introduce others?

As it is, the dict datastructure is somewhere in the uneasy middle between an opaque datastructure and a “non-local datastructure-by-convention” like a list, which is supposed to maintain some invariants regarding its graph structure, but there is no guarantee that all the code succeeds in doing so during transformations (something which opaque datastructures are meant to guarantee, of course).

Feel free to :slight_smile:

As is, SWI-Prolog does not really provide opaque data structures. Blobs are not a good alternative as they are immutable. They can at best point to an external data structure that in turn can be mutable, but is outside Prolog’s direct reach and thus cannot contain normal Prolog terms.

Once upon a time I have considered adding something to achieve that. The basic idea would be that a data structure is associated to a module and only that module can see the internals. Other modules only see an opaque data structure. It is an old pending idea. Never really found the need to realize it.

2 Likes

Opaque data type - Wikipedia

In computer science, an opaque data type is a data type whose concrete data structure is not defined in an interface. This enforces information hiding, since its values can only be manipulated by calling subroutines that have access to the missing information. The concrete representation of the type is hidden from its users, and the visible implementation is incomplete. A data type whose representation is visible is called transparent .

Yes. Prolog in contrast is very open and trusty … every term is an inspectable, directed (possibly cyclic) graph. It was another epoch :smiley:

Random idea: How do you do information hiding in such a system? Using continuation-passing style, untrusted code can be run by manager code (Inversion of Control style), with commands passed to the manager when “yield” is called by the untrusted code and the manager calling the continuation after binding variables only the ids to terms, not the terms themselves. A nice exercise for the classroom?

But if it were to work, it could be used to explain call/n.

But you might have to make compound_name_arguments/3 generate solutions when going rightwards:

As in (hypothetically):

?- compound_name_arguments(f(a,b,c),Name,Args).
Name = f, Args = [a, b, c];
Name = f(a), Args = [b, c];
Name = f(a,b), Args = [c];
Name = f(a,b,c), Args = [];

Sometimes I feel there should be a special annotation to trigger a once/1 call automatically without having to enclose a predicate in a once/1 call … predicates can then plausibly claim to stand for relations generate template solution while one would still mostly call them with semi-deterministic behaviour.

But somehow it doesn’t matter since interestingly:

In SWI-Prolog, the f and f() at goal (and function) positions are actually synomymous:

https://eu.swi-prolog.org/pldoc/man?section=ext-compound-zero

For predicates, goals and arithmetic functions (evaluable terms), and () are equivalent .

?- X is pi.
X = 3.141592653589793.

?- X is pi().
X = 3.141592653589793.