Some thoughts on dicts in SWIPL 7

Bob Minors <[email protected]>
Newsgroups gmane.comp.ai.prolog.swi
Message-ID <[email protected]>
As a learning exercise, I've been converting an existing program to use
dicts in place of some rather clumsy ordered list structures. It's going
quite well - I like dicts - and it's given rise to some thoughts that
I'd like to share. Two little things and a broad question about
programming style.

The original data structure was an ordered list of 27 elements (some of
which were themselves ordered lists) and, when calling predicates on it,
I would pick out the ones I wanted using nth0. Something like this:

    foo(X,Y) :- nth0(12,X,Apples), nth0(23,X,Bananas),
foo1(Apples,Bananas,Y).

This worked fine, but it's always been ugly and tricky to maintain. Now
I've replaced the whole X structure with a dict and I can do something
like this:

    foo(X,Y) :- foo(X.apples,X.bananas,Y).

which is simpler, more readable and more maintainable. That's the
motivation.

One thing I needed to do in several places, was to create empty dicts
with given tags and given lists of keys but with all the values
uninstantiated, ready for filling in later. There's no built-in function
that does quite what I wanted, so this was how I coded it:

    free_pair(X,X:_).

    blank_dict(Tag,Keylist,Dict) :-
        maplist(free_pair,Keylist,Pairs),
        dict_create(Dict,Tag,Pairs).

    12 ?- blank_dict(foo,[a,b,c],Dict).
    Dict = foo{a:_G1029, b:_G1035, c:_G1041}.

It's not a huge thing, it's used in just seven places in my program but
it saves typing ":_" over and over again (in one case, 27 times). Is
this something that would often be done with dicts? Would it be worth
having a built-in function for it? Just a suggestion.

Next thing: one of the values in the top-level dict is a list of smaller
dicts, representing a FIFO queue of elements waiting to be processed,
and there is a predicate that needs to check one of the key values in
the dict at the head of the list to see if it's ready for processing.
I've defined a function that plucks out head of the list:

    X.head(Tag) := H :- X.Tag=[H|_].

so I can write something like this:

    process_queue(X) :- X.head(queue).is_ready(), process_queue1(X.queue).

where the is_ready() is defined elsewhere as a function on a queue
element dict. This works and is easy to read (for me, at least) but it's
looking more like functional programming than conventional Prolog and
I'm starting to feel the ground shifting under my feet. Is this a bad
thing? Probably not.
 
Now a stylistic question. When writing predicates that call smaller
predicates, there is a choice between of pulling out the relevant fields
from the dict at the top level and passing them down as variables to the
lower-level predicates, or simply passing down the whole dict and
letting the lower-level predicates take what they want. One the one
hand, something like

    foobar(X,FooBar) :- foo(X.a, X.b, Foo), bar(X.c, X.d, X.e, Foo, Foobar).

or this:

    foobar(X,Y) :- foo(X,Foo), bar(X,Foo,FooBar).

The first scheme keeps all the structural detail of X at the top level
and the lower level predicates (foo and bar) don't need to know about
it, so if I change the top-level structure they should be unaffected and
that seems good from a module point of view; but the second scheme might
be more maintainable if, say, foo is later improved and needs to
reference X.d after all - this can be done without changing the
top-level calling predicates, which makes the top level more robust.
Having thought about it, I've come to the conclusion that it depends on
which is expected to be the more stable and which the more volatile
during program development - top-level dict or lower-level modules.
Top-down design vs bottom-up coding. No right answer.

Thoughts welcome.

Bob Minors
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.