Могу ли я в Mercury объявить лямбду как имеющую тот же детерминизм, что и модус предиката, содержащего лямбду?
Вот что я пытаюсь сделать. Я написал функцию fold (ниже), которая работает с типом array2d. fold
вызывает предоставленный вызывающей стороной предикат для каждого элемента в массиве. Он отлично работает, пока принимает только предикат det в качестве аргумента.
:- 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).
Но я хочу, чтобы fold
принимал предикат det или semidet (и терпел неудачу, если какой-либо вызов предиката терпит неудачу). Раскомментирование строки mode ... is semidet
приводит к ошибкам компилятора, которые я не знаю, как решить. Проблема в том, что лямбда-выражения в fold
объявлены как det, поэтому они не могут вызывать semidet Pred
. Если я поменяю лямбды на полудет, то fold
в целом не может быть дет.
Как я могу решить эту проблему? Кажется, самым простым подходом было бы объявить, что лямбда-выражения наследуют свой детерминизм от предиката fold
— поэтому они являются det, когда fold
используется как det, аналогично для semidet — но я не знаю, возможно ли это. .
Другой подход, конечно, состоит в том, чтобы превратить FoldRows
и FoldCols
в именованные предикаты (не лямбда-выражения) с несколькими режимами. Но это быстро становится неэлегантным, и мне интересно, есть ли более простое решение.