lambdanon-deterministicmercury

Matching determinism of a lambda and a predicate in Mercury


In Mercury, can I declare a lambda as having the same determinism as the mode of the predicate that contains the lambda?

Here's what I'm trying to do. I wrote a fold function (below) that works on the array2d type. fold calls a caller-supplied predicate for each element in the array. It works fine, as long as it only accepts a det predicate as an argument.

:- pred fold(array2d(T), pred(T,  int, int, A, A),              A,  A).
:- mode fold(in,         pred(in, in,  in, in, out) is det,     in, out) is det.
% Uncommenting the next line causes mode errors during compilation
% :- mode fold(in,       pred(in, in,  in, in, out) is semidet, in, out) is semidet.

fold(Array, Pred, !Accumulator) :-
    bounds(Array, NumRows, NumCols),

    FoldRows = (pred(RowNumber :: in, RowAccIn :: in, RowAccOut :: out) is det :-
        FoldCols = (pred(ColNumber :: in, ColAccIn :: in, ColAccOut :: out) is det :-
            Value = Array^elem(RowNumber, ColNumber),
            Pred(Value, RowNumber, ColNumber, ColAccIn, ColAccOut)
        ),
        int.fold_up(FoldCols, 0, NumCols - 1, RowAccIn, RowAccOut)
    ),
    int.fold_up(FoldRows, 0, NumRows - 1, !Accumulator).

But I want fold to accept either a det or a semidet predicate (and fail if any call to the predicate fails). Uncommenting the mode ... is semidet line give me compiler errors that I don't know how to resolve. The problem is that the lambdas in fold are declared as det, so they can't call a semidet Pred. If I change the lambdas to semidet, then fold as a whole can't be det.

How can I resolve this? It seems like the most straightforward approach would be to declare that the lambdas inherit their determinism from the fold predicate -- so they're det when fold is being used as det, likewise for semidet -- but I don't know if that's possible.

Another approach, of course, would be to turn FoldRows and FoldCols into named predicates (not lambdas) with multiple modes. But that quickly becomes inelegant, and I'm wondering if there's a more straightforward solution.


Solution

  • Mercury can sometimes infer the modes and determinisms of predicates, so I initially tried this by omitting the determinism declaration from the lambda expressions. However Mercury's lambda syntax doesn't allow me to do this, so this inference cannot be used with lambdas.

    I'm afraid that the only solution is, as you guessed, to turn FoldRows and FoldCols into multi moded named predicates.