Mercury:如何声明高阶数据类型的确定性?
当我编译下面的 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 ofTestCase
'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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
Ben 是对的,
我想补充一点,Mercury 假设函数默认是确定性的(函数应该是确定性的,这是明智的)。对于必须声明决定论的谓词来说,情况并非如此。与任何其他函数或谓词相比,这使得使用确定性函数进行高阶编程变得更加容易,仅仅是因为样板代码更少。
因此,您可以使 lambda 表达式更加简洁。您还可以通过将头部中的变量 S 替换为主体,将 lambda 表达式的主体移动到头部。
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.
我也花了一段时间才掌握窍门。
问题在于高阶项的众数不是其类型的一部分。因此没有声明类型确定性的语法。众数中包含高阶项的决定论。
在您的示例中,
assert_equals
的第一个参数的类型为test_case(T)
,但模式为in
。这意味着该函数是semiset
的事实丢失了。我不确定如果您传递的函数是 det ,它是否真的可以正确编译或运行;即使在这种情况下,模式也不应该处于in
状态。下面是一个示例:
如您所见,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 typetest_case(T)
, but has modein
. This means that the fact that the function issemidet
gets lost. I'm not sure if it would actually compile or run correctly if the function you're passing wasdet
; even in that case the mode really shouldn't bein
.Here's an example:
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 modein
and the function result has modeout
, and that its determinism isdet
.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 usualio
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.
要回答第二个问题,错误消息中的
/* 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 toassert_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.