New notation for dict-based "method indicators"?

As SWI-Prolog provides methods on dicts. These are also functions because they evaluate to some term, but functions are generally not understood to be attached to a parent object or evaluated in the context of an object. Some new terminology is hereby proposed:

Call them “methods” (and the call a “dotcall”)

And write the corresponding “method indicator” as

.name/arity (or maybe name./arity),

in a manner analogous to name/arity (predicate indicator) and name//arity (DCG non-terminal indicator).

I note that this object-oriented programming is a bit weakened by the fact that dict tags must also be a module name. Dict tags thus are actually “method bundle names”. If one really wants OO, the classname must be carried inside the dict for some extra infrastructure. That’s probably what Logtalk does.

(post withdrawn by author, will be automatically deleted in 24 hours unless flagged)

To be honest, I rarely use this. I wonder how useful the dict methods are considered to be. Anyone here with experience?

I use get_dict/3 a certain amount for conditional lookup. (And in a few cases, for custom versions of get_dict_ex/3 that provide better error messages.)
But most of the time, I use the “.” operator because it’s a more compact notation. (I have to be careful if I use it in the head of a clause, of course).

Function on dicts could use a wiki. :slightly_frowning_face:

1 Like

Actually the official name is “function on dicts” and not dict methods.
To become dict methods you would need to show that they can deal
with method dispatch and favorably inheritance/overiding, things from

object-oriented programming. Can you show that?

BTW: Python has evolved. An object state need not only be __dict__,
but can also be __slots__ or a combination of __dict__ and __slots__ .
Slots seem to be more memory savy and faster than dicts. But they

require a slot definition on class level. I dunno yet whether there is some
feasible way to bring this to Prolog. I had an old idea to compress dicts
as follows. Currently SWI-Prolog uses this representation for dicts:

?- X=foo{bar:1,baz:2}, X=..L.
X = foo{bar:1, baz:2},
L = [C'dict', foo, 1, bar, 2, baz].

But you could also use this representation:

?- X=foo{bar:1,baz:2}, X=..L.
X = foo{bar:1, baz:2},
L = [C'dict', [foo, bar, baz], 1, 2].

Putting the list of slots into the first argument. This would
open up that different dicts could share the same slot list, making
the representation more memory savy like in Python. But I guess

it doesn’t give necessarely some speed advantage. Unless
you can compile something like:

    Y = X.bar

Into this here:

   arg(3, X, Y)

But the compilation problem into arg/3 for example would be more
or less the same for compressed and none compressed dicts.
You would need some reliable inference of the class of X and you

need single inheritance, wouldn’t work anymore for multiple
inheritance. Here is a SO post detailing new __slots__ .

Usage of __slots__?
https://stackoverflow.com/a/28059785

You could use (//)/2 , because functions on dicts adds two arguments,

Yes, but that’s not a good idea, because the // already indicates “DCG rule”.

It is already sufficiently perverse that one is told both of these:

  • name//arity is syntax for the “non-terminal indicator”, i.e. DCG rules, and means “arity + 2”
  • There is no guarantee that a DCG implementation necessarily be based on two additional arguments.

There should be abstraction.

Actually the official name is “function on dicts” and not dict methods. To become dict methods you would need to show that they can deal with method dispatch and favorably inheritance/overiding, things from object-oriented programming. Can you show that?

I suppose so. But there is no inheritance “out of the box” - all the “dict objects” are on the same level (they really are modules, which are not nested). Inside the module, you sure can dispatch on argument “type” (if it is recognizable to Prolog in-the-head; this means the argument must be tagged at least or you are forced to manually dispatch in the dict-attached predicate/function.

For inheritance of whatever kind, one either has to instrument the dict tag (it can take any term) or add special key-value pairs to the dict. Should not be too hard, but definitely based on convention, not provided by the language.

But it’s probably easier to just use Logtalk if one wants to go that way. It also has objects implemented as modules (“modules are prototypes”). Makes sense.

I’m still in the exploratory phase :smiley:

Inline expressions seems a good use:

:- module(string,[]).

% This is a predicate format/3 written as a function format/2 (maybe there
% needs to be a new descriptor syntax: format///2 ?) taking a dict tagged as
% "string" and two arguments which evaluates to the value of variable Text.

S.format(Msg,Args) := Text :- with_output_to(string(Text),format(Msg,Args)).

and then write inline expressions like:

?- debug(foo),debug(foo,string{}.format("Hello, ~q\n",["World"]),[]).
% Hello, "World"

I made a little test, using the new except/1 discovery, a SWI-Prolog
feature I have overlooked so far. And I could let dicts do method
dispatch and inheritance/overriding without warnings:

File “dog”:

:- module(dog, [barking/2]).
_.barking() := woof.

File "beagle"

:- module(beagle, []).
:- reexport(dog).

File “rottweiler”:

:- module(rottweiler, [barking/2]).
:- reexport(dog, except([barking/2])).
_.barking() := ruff.

The trick was loading all Prolog modules and bypassing the
trouble maker pseudo Prolog module “user”. So I wrote a Prolog text
“main” and used except/1 again:

File “main”:

:- module(main, []).
:- use_module(beagle).
:- use_module(rottweiler, except([barking/2])).

You only need to consult “main”. It wont show any warnings.
Then a query works like a charm:

?- X = beagle{}.barking().
X = woof.

?- X = rottweiler{}.barking().
X = ruff.

It has to do with that fact that a qualified call M:G has some
special rights, that bypass all import tightenings. So although
main wants barking/2 uniquely and forces me once again

to use except/1. This is irrelevant when qualified calls M:G
are used, which are used under the hood in the implementation
of functions on dict. In SWI-Prolog, as soon as a module M

is loaded, it is globally visible for all qualified calls M:G.

2 Likes

I don’t know whether modules are also visible across pengines?
Or what SWISH would do with it. Didn’t test yet. In Java this can be
solved via class loaders. The example I was using was:

image005

But I adapted it so that insted a binary barking predicate is
defined, I used function on dicts notation via (:=)/2 to define
something, also instead of message sending, I used

the dict dot operator to call something.

1 Like