Logic 汞的ADT性质

Logic 汞的ADT性质,logic,declarative,mercury,Logic,Declarative,Mercury,我不明白为什么Mercury(10.04)不能推断出下一个片段的决定论: :- pred load_freqs(int::in, io.res(list(float))::out, io::di, io::uo) is det. load_freqs(CPU, ResFreqs, !IO):- open_input(cpu_fn(CPU, "available_frequencies"), ResStream, !IO), (ResStream = io.ok(Stream) -

我不明白为什么Mercury(10.04)不能推断出下一个片段的决定论:

:- pred load_freqs(int::in, io.res(list(float))::out, io::di, io::uo) is det.
load_freqs(CPU, ResFreqs, !IO):-
    open_input(cpu_fn(CPU, "available_frequencies"), ResStream, !IO),
    (ResStream = io.ok(Stream) ->
        ResFreqs = io.ok([])
    ;ResStream = io.error(Err),
        ResFreqs = io.error(Err)
    ).
它抱怨说:

cpugear.m:075: In `load_freqs'(in, out, di, uo): cpugear.m:075: error: determinism declaration not satisfied. cpugear.m:075: Declared `det', inferred `semidet'. cpugear.m:080: Unification of `ResStream' and `io.error(Err)' can fail. cpugear.m:076: In clause for predicate `cpugear.load_freqs'/4: cpugear.m:076: warning: variable `CPU' occurs only once in this scope. cpugear.m:078: In clause for predicate `cpugear.load_freqs'/4: cpugear.m:078: warning: variable `Stream' occurs only once in this scope. 更新#1: 即使在以下情况下,它也可以决定det:

:- pred read_freqs(bool::in, io.res(io.input_stream)::in, io.res(list(float))::out, io::di, io::uo) is det.
read_freqs(no, ResStream, io.ok([]), IO, IO):- ResStream = io.ok(_).
read_freqs(F, io.ok(_), io.ok([]), IO, IO):- F = yes.
read_freqs(yes, io.error(Err), io.error(Err), IO, IO).
read_freqs(F, ResStream, io.error(Err), IO, IO):- ResStream = io.error(Err), F = no.

我对Mercury的条件决定论规则(见下文)的理解是,要将其视为确定性,您应该将
->
替换为

汞参考手册:

If-then-else的条件 不能失败,if-then-else是 相当于 条件和“然后”部分,及其 决定论据此计算。 否则,如果 “then”部分或“else”部分 部分可能会失败


好的,它可以推断出:

:- pred load_freqs(int::in, io.res(list(float))::out, io::di, io::uo) is det.
load_freqs(CPU, ResFreqs, !IO):-
    open_input(cpu_fn(0, "available_frequencies"), ResStream, !IO),
    (ResStream = io.ok(Stream),
        ResFreqs = io.ok([])
    ;ResStream = io.error(Err),
        ResFreqs = io.error(Err)
    ).

但是为什么“if-then-else”结构引入了semidet?

至于“为什么”。让我们用if-then-else查看原始代码:

(ResStream = io.ok(Stream) ->
    ResFreqs = io.ok([])
;ResStream = io.error(Err),
    ResFreqs = io.error(Err)
).
如果条件失败,则else情况下的第一个连接是semidet测试。编译器不知道它必须成功(可以通过知道此条件失败来推断)。换句话说,编译器不够聪明

也就是说,很少会发现这个问题,因为通常情况下,条件更复杂,不允许进行这种推断,因此编译器不够聪明,无法确定正确的决定论并不重要


建议尽可能使用开关编程(如本例),这样可以防止当前问题,并有助于确保您已涵盖所有可能的ResStream案例。例如,如果将来io.error被重新分解,可能是io.error\u文件未找到或io.error\u磁盘已满等,编译器将指示程序员修复其开关,因为它现在不完整。

在这种情况下,
ResStream
可以与
io.ok(Stream)
统一,但也可以与
io.error(Err)
统一。这取决于打开文件的结果。这有两个分支。获得独家“或”我使用“如果其他”结构,并根据您的引用,它应该适合。但是Mercury不能推断
不是(ResStream=ok()))=>ResStream=error()
。在你的例子中,else分支是
ResStream=io.error(Err),ResFreqs=io.error(Err)
,如果ResStream是io.ok(X),它将失败。我引用的规则没有说明任何关于添加条件的否定。我同意逻辑上它可以被添加,但参考手册表明它不是。相反,如果析取有一个“开关”的形式,则会被特别处理——它似乎允许析取中的连词,但不允许条件句。我猜原因是,在普通情况下,模式不相交,在这种情况下,“和“->”做同样的事情。模式可以相交,Mercury发现这些相交与推断决定论“多重”的相交。请参阅其他示例。如果您仅通过head决定det.,则
read\u freqs
(更新中)将被标记为multi。我对“为什么?”的第一个回答是因为Mercury参考手册中说应该这样做。但是,我猜你的意思是“为什么水星是这样设计的?”。我的猜测是,开关需要以一种特殊的方式进行处理,设计师不想让它们变得比必要的更复杂,避免负面信息确实简化了事情。另外,我的印象是,Mercury通常鼓励优先于条件句的转换。事实上,有一个开关--INFORT ite而不是switch可以报告应该被开关替换的条件。谢谢,这是一个有用的选项--“INFORT ite而不是switch”。现在,对我来说,通过
(X=yes,true;not(X=yes),X=no)
(这是我对
的期望)(X=yes->true;X=no)
)将被推断为未设置,因为
不是/1
。这是真的,直到条件变得更复杂。如果符号多于yes或no,或者条件中有多个统一。
(ResStream = io.ok(Stream) ->
    ResFreqs = io.ok([])
;ResStream = io.error(Err),
    ResFreqs = io.error(Err)
).