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.
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.