4

当我编译下面的 Mercury 代码时,我从编译器中得到这个错误:

In clause for `main(di, uo)':
  in argument 1 of call to predicate
  `test_with_anonymous_functions.assert_equals'/5:
  mode error: variable `V_15' has
  instantiatedness `/* unique */((func) =
  (free >> ground) is semidet)',
  expected instantiatedness was `((func) =
  (free >> ground) is det)'.

我认为编译器的意思是“当你声明类型test_case时,你没有指定确定性,所以我认为你的意思是det。但是你传入了一个semidetlambda。”

我的问题:

  • 声明类型的确定性的语法是什么?我尝试过的猜测都导致了语法错误。
  • 有人可以解释' 实例化的/* unique */部分是什么意思吗?TestCase这会导致给定和预期实例化之间的不匹配吗?
  • 有没有更简洁的方式来声明 lambda main?我对 lambda 的声明与我在 lambda 中的代码一样多。

编码:

% (Boilerplate statements at the top are omitted.)

% Return the nth item of a list
:- func nth(list(T), int) = T.
:- mode nth(in,      in)  = out is semidet.
nth([Hd | Tl], N) = (if N = 0 then Hd else nth(Tl, N - 1)).

% Unit testing: Execute TestCase to get the 
% actual value. Print a message if (a) the lambda fails
% or (b) the actual value isn't the expected value.
:- type test_case(T) == ((func) = T).
:- pred assert_equals(test_case(T), T,  string, io.state, io.state).
:- mode assert_equals(in,           in, in,     di,       uo) is det.
assert_equals(TestCase, Expected, Message, !IO) :-
    if   Actual = apply(TestCase), Actual = Expected
    then true % test passed. do nothing.
    else io.format("Fail:\t%s\n", [s(Message)], !IO).

main(!IO) :-
    List = [1, 2, 3, 4],
    assert_equals( ((func) = (nth(List, 0)::out) is semidet),
                 1, "Nth", !IO).
4

3 回答 3

4

本是对的,

我想补充一点,Mercury 默认假设函数是确定性的(函数应该是确定性的,这是明智的)。对于必须声明确定性的谓词,情况并非如此。这使得使用确定性函数进行高阶编程比任何其他函数或谓词更容易,仅仅是因为样板更少。

因此,您可以使您的 lambda 表达式更加简洁。您还可以通过将头部中的变量 S 替换为主体来将 lambda 表达式的主体移动到头部。

apply_transformer((func(S0) = "Hello " ++ S0),
                  "World", Msg),
于 2011-09-12T10:48:11.217 回答
3

我也花了一段时间才掌握了窍门。

问题是高阶项的众数不是其类型的一部分。因此,没有用于声明类型确定性的语法。模式中承载了高阶项的确定性。

在您的示例中,第一个参数assert_equals具有 type test_case(T),但具有 mode in。这意味着函数semidet丢失的事实。如果您传递的函数是,我不确定它是否会真正编译或正确运行det;即使在那种情况下,模式真的不应该是in.

这是一个例子:

:- pred apply_transformer(func(T) = T, T, T).
:- mode apply_transformer(func(in) = out is det, in, out).
apply_transformer(F, X0, X) :-
    X = F(X0).

main(!IO) :-
    apply_transformer((func(S0) = S is det :- S = "Hello " ++ S0),
                      "World", Msg),
    print(Msg, !IO),
    nl(!IO).

如您所见,第一个参数的类型apply_transformer仅表示它是一个高阶函数,它接受一个参数并返回相同类型的结果。正是模式声明实际上说函数参数具有模式in并且函数结果具有模式out,并且它的确定性是det

我相信/*unique */错误消息的一部分表明编译器认为它是一个唯一值。我不确定这是否是一个问题,因为除了通常的io状态之外,您没有在任何地方使用独特的模式。

至于 lambda 语法,不幸的是,我认为你不能做得更好。我发现 Mercury 中 lambda 的语法相当不令人满意。它们非常冗长,以至于我通常最终只为除了最微不足道的 lambda 之外的所有函数/谓词创建命名函数/谓词。



于 2011-09-12T00:23:35.397 回答
2

要回答第二个问题,/* unique */错误消息中的 是指调用 to 的第一个参数assert_equals,这是您刚刚构造的 lambda 项。这是唯一使用该术语的地方,因此在调用时对它的引用是唯一的。

唯一的 inst 与地面 inst 匹配(但反之亦然),因此在这种情况下,唯一性不会导致不匹配。问题在于决定论。

于 2014-10-05T14:01:09.930 回答