[m-users.] Functions vs Predicates?

Zoltan Somogyi zoltan.somogyi at runbox.com
Thu Oct 29 14:17:32 AEDT 2020


2020-10-29 13:49 GMT+11:00 "Julien Fischer" <jfischer at opturion.com>:
>> What confuses me further is that there are e.g. functions like
>> `univ_to_term` and `term_to_univ`
>> which seem like perfect candidates to have been written as a single
>> reversible `term_univ(Term, Univ)` predicate instead.
> 
> The function term_to_univ does not exist.

But type_to_univ and univ_to_type do exist; these are probably
what the original poster was referring to. And they *are* each
other's inverses; indeed, one is implemented by calling the other.

> That said: I would argue that it is easiser to comprehend the intent
> of code using the former two names and that such code is unlikely to
> be usefully reversible anyway.
> 
> Aside: I find that most introductory logic programming / Prolog texts tend
> to overrate the importance of being able to make operations reversible;
> it's a cute trick but of very limited utility in non-trivial programs.

Agreed.

>> When is it more idiomatic to use a function, and when to use a predicate?
> 
> The language definition is mostly agnostic on that point; to some extent
> it tries to encourage you to treat function as functions (e.g. through the
> existence of the existence of the default function modes etc).
> 
> In the Mercury implementation itself, the usage we typically follow is:
> 
>    * Deterministic operations with a single output are functions.
> 
>    * Operations that can fail are predicates.
> 
>    * Operations with multiple outputs are predicates (not functions that
>      return tuples as in, for example, Haskell).

That is all true, but it is not complete. There are two circumstances I can think of
off the top of my head where we use predicates even for deterministic operations
that have just one output.

The first is when that output is an updated state. For example, map.delete, which
deletes a key from a map, has both function and predicate forms. We tend to use
the predicate form because map.delete(Key, !Map) is easier to read than
!:Map = map.delete(!.Map, Key).

The second is that if two operations are very closely related, such as map.search
and map.lookup, but one is semidet, then we prefer using the predicate form
of the semidet operation (Julien's second point above), and we *also* prefer
the predicate form of the other just to minimize unnecessary differences
between invocations of the two ops.

Note that the Mercury standard library is intended to support not just our own
programming style, but everyone's, which it why has both function and predicate
forms of each operation, unless there are compelling reasons against one or the
other.

Zoltan.


More information about the users mailing list