[m-users.] Why does the standard library not define many multi-mode functions?

Zoltan Somogyi zoltan.somogyi at runbox.com
Sat Oct 23 18:32:36 AEDT 2021


2021-10-19 09:26 GMT+11:00 "Philip White" <philip at pswhite.org>:
> One of the advantages of logic programming, in my understanding, is
> that by defining computation in terms of relations, you both reduce the
> amount of implementation that you have to do, and you can reduce the
> size of your API.

I presume that by "size of API" you mean the number of predicates, functions or
class methods in that API (whichever applies in the language at hand).

You are right in that replacing e.g. two predicates, each with one mode,
with a single predicate with two modes does in some sense reduce
the size of the API. However, size is not the most important criterion;
others, such as complexity and usability, are more important
to users of the API. For example, ...

> For example, [find_and_remove] and [add] are just two
> ways of looking at the same relation defined on sets. Even if the
> implementation of these two modes has to be different for performance
> or other reasons, the [map] module could still expose them under the
> same predicate by using [promise_equivalent_clauses].

... a programmer who wanted to remove an item from a set would
not usually start looking at predicates whose names say that they
*add* to a set. In fact, having to do removal from a set by calling
a predicate named "add" would be considered obfuscation by
many people, including us. So if we followed the course of action
you advocate, our API would have one fewer predicate, but would
be harder to use.

> Doing that then
> enables any user of the [map] module to write relations with multiple
> modes that only require a single implementation.

In more than a quarter century of Mercury programming, I have never
wanted or needed to write code that could do that. But if someone
wants or needs to do that, they don't need the help of the Mercury
standard library: they can write the required predicates themselves,
complete with promise_equivalent_clauses pragmas.

> Thus, I'm a little saddened whenever I look at the mercury standard
> library APIs. It seems to me that, although many predicates have a
> bunch of different determinisms, and some have different modes, most of
> the time the API looks quite similar in structure to a functional
> language's API,

That is because semantically,  due to the absence of a Herbrand constraint
solver, Mercury is closer to Haskell than to Prolog.

> and it appears very difficult to write multi-mode
> predicates over maps.

That has almost nothing to with the standard library. Writing multidirectional
code for any nontrivial task is made extremely hard by the fact that
if-then-elses implicitly restrict the directions in which data can flow
in a clause. You can transmit bindings from the condition to the then-part,
but you cannot transmit bindings from the then-part to the condition.
This is as true in Prolog as it is in Mercury, and for the same reason:
until you know whether the condition succeeds or not, you don't even
know whether the then-part should be executed or not.

Given this fact, for almost any nontrivial task you want to make multidirectional,
you are better off writing separate implementations for each direction,
and (if needed, which it often isn't) tell the compiler that semantically
they are equivalent. The presence or absence of multidirectional predicates
in the standard library is pretty much irrelevant in this process.

Zoltan.


More information about the users mailing list