Mercury:如何声明高阶数据类型的确定性?

发布于 2024-12-04 01:59:38 字数 1676 浏览 1 评论 0原文

当我编译下面的 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。但是你传入了一个semiset lambda。”

我的问题:

  • 声明类型的确定性的语法是什么?我尝试过的猜测都导致了语法错误。
  • 有人可以解释 TestCase 实例化的 /* unique */ 部分意味着什么吗?这会导致给定的和给定的之间不匹配吗?预期的实例化?
  • 有没有更简洁的方式在 main 中声明 lambda?我对 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).

When I compile the Mercury code below, I get this error from the compiler:

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

I think what the compiler is saying is "When you declared the type test_case, you didn't specify a determinism, so I assumed you meant det. But then you passed in a semidet lambda."

My questions:

  • What's the syntax for declaring the determinism of a type? The guesses I've tried have all resulted in syntax errors.
  • Can someone explain what the /* unique */ part of TestCase's instantiatedness means? Will that cause a mismatch, between the given & expected instantiatedness?
  • Is there a less verbose way of declaring the lambda in main? I have as much declaration about the lambda as I do code inside the lambda.

The code:

% (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).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

掩饰不了的爱 2024-12-11 01:59:38

Ben 是对的,

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

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

apply_transformer((func(S0) = "Hello " ++ S0),
                  "World", Msg),

Ben is right,

I'd like to add that Mercury assumes that functions are deterministic by default (it's sensible that functions ought to be deterministic). This is not true for predicates in which the determinism must be declared. This makes it easier to do higher order programming with deterministic functions than any other function or predicate, simply because there's less boilerplate.

Because of this, you are able to make your lambda expression a little bit more concise. You can also move the body of the lambda expression into the head by substituting the variable S in the head with the body.

apply_transformer((func(S0) = "Hello " ++ S0),
                  "World", Msg),
溺孤伤于心 2024-12-11 01:59:38

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

问题在于高阶项的众数不是其类型的一部分。因此没有声明类型确定性的语法。众数中包含高阶项的决定论。

在您的示例中,assert_equals 的第一个参数的类型为 test_case(T),但模式为 in。这意味着该函数是 semiset 的事实丢失了。我不确定如果您传递的函数是 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 之外的所有函数/谓词创建命名函数/谓词。



This took me a while to get the hang of as well.

The problem is that the mode of a higher-order term is not part of its type. So there is no syntax for declaring the determinism of a type. The determinism of a higher-order term is carried in the mode.

In your example, the first argument of assert_equals has type test_case(T), but has mode in. This means that the fact that the function is semidet gets lost. I'm not sure if it would actually compile or run correctly if the function you're passing was det; even in that case the mode really shouldn't be in.

Here's an example:

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

As you can see, the type of the first argument to apply_transformer only says that it is a higher-order function takes one argument and returns a result of the same type. It's the mode declaration that actually says the function parameter has mode in and the function result has mode out, and that its determinism is det.

I believe the /*unique */ bit of the error message says that the compiler thinks it is a unique value. I'm not sure whether that's a problem or not, since you're not using unique modes anywhere other than the usual io state.

As for the lambda syntax, I don't think you can do any better unfortunately. I find the syntax of lambdas in Mercury to be fairly unsatisfactory; they're so verbose that I usually end up just making named functions/predicates instead for all but the most trivial lambdas.



定格我的天空 2024-12-11 01:59:38

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

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

To answer the second question, the /* unique */ in the error message is referring to the first argument of the call to assert_equals, which is the lambda term you have just constructed. This is the only place the term is used, hence the reference to it is unique at the point of call.

A unique inst matches a ground inst (but not vice-versa), so in this case the uniqueness will not cause a mismatch. It's the determinism that is the problem.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文